<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="keywords" content="Gridea静态个人博客">
<meta name="description" content="温故而知新">
<meta name="theme-color" content="#000">
<title>林星</title>
<link rel="shortcut icon" href="/favicon.ico?v=1720431965986">
<link rel="stylesheet" href="/media/css/mist.css">
<!-- start custom -->
<link rel="stylesheet" href="/media/css/custom.css">
<!-- end custom -->
<link rel="stylesheet" href="/media/fonts/font-awesome.css">
<link
  href="//fonts.loli.net/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|Rosario:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext"
  rel="stylesheet" type="text/css">

<link href="/media/hljs/styles/default.css"
  rel="stylesheet">

<link rel="stylesheet" href="/styles/main.css">
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<script src="/media/js/jquery.js"></script>
<script src="/media/hljs/highlight.js"></script>
<script src="https://cdn.jsdelivr.net/npm/velocity-animate@1.5.0/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/velocity-animate@1.5.0/velocity.ui.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
  integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">


<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
  integrity="sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js"
  integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous"
  onload="renderMathInElement(document.body);"></script>






</head>

<body>
  <div class="head-top-line"></div>
  <div class="header-box">
    
<div class="mist">
  <header class="header bg-color ">
    <div class="blog-header box-shadow-wrapper  " id="header">
      <div class="nav-toggle" id="nav_toggle">
        <div class="toggle-box">
          <div class="line line-top"></div>
          <div class="line line-center"></div>
          <div class="line line-bottom"></div>
        </div>
      </div>
      <div class="site-meta">       
        <div class="site-title">
          
            <a href="/" class="">
              <span class="logo-line-before">
                <i class=""></i>
              </span>
              <span class="main-title">林星</span>
              <span class="logo-line-after">
                <i class=""></i>
              </span>
            </a>  
          
        </div>
        
      </div>
      <nav class="site-nav" id="site_nav">
        <ul id="nav_ul">
          
            
            
              
            
            <li class="nav-item ">
              
              
                <a href="/" target="_self">
                  <i class="fa fa-globe"></i> 首页
                </a>
              
            </li>
          
            
            
              
            
            <li class="nav-item ">
              
              
                <a href="/archives" target="_self">
                  <i class="fa fa-globe"></i> 归档
                </a>
              
            </li>
          
            
            
              
            
            <li class="nav-item ">
              
              
                <a href="/tags" target="_self">
                  <i class="fa fa-globe"></i> 标签
                </a>
              
            </li>
          
            
            
              
            
            <li class="nav-item ">
              
              
                <a href="/website-fairy" target="_self">
                  <i class="fa fa-globe"></i> 网站推荐
                </a>
              
            </li>
          
            
            
              
            
            <li class="nav-item ">
              
              
                <a href="/about-starling" target="_self">
                  <i class="fa fa-globe"></i> 关于
                </a>
              
            </li>
          
          
            
              <li class="nav-item ">
                <a href="/friends/" target="_self">
                  
                    <i class="fa fa-address-book"></i> 友链
                  
                </a>
              </li>
            
          
          
            <li id="fa_search" class="nav-item">
              <a href="javascript:void(0);">
                <i class="fa fa-search"></i> <span class="language" data-lan="search">搜索</span>
              </a>
            </li>
          
        </ul>
      </nav>
    </div>
  </header>
</div>

<script type="text/javascript"> 
 
  let showNav = true;

  let navToggle = document.querySelector('#nav_toggle'),
  siteNav = document.querySelector('#site_nav');
  
  function navClick() {
    let sideBar = document.querySelector('.sidebar');
    let navUl = document.querySelector('#nav_ul');
    navToggle.classList.toggle('nav-toggle-active');
    siteNav.classList.toggle('nav-menu-active');
    if (siteNav.classList.contains('nav-menu-active')) {
      siteNav.style = "height: " + (navUl.children.length * 42) +"px !important";
    } else {
      siteNav.style = "";
    }
  }

  navToggle.addEventListener('click',navClick);  
</script>
  </div>
  <div class="main-continer">
    
    <div
      class="section-layout bg-color mist">
      <div class="section-layout-wrapper">
        <div id="sidebarMeta" class="sidebar">
    
<div class="sidebar-wrapper box-shadow-wrapper ">
  <div class="sidebar-item">
    <img class="site-author-image right-motion" src="/images/avatar.png"/>
    <p class="site-author-name">林星</p>
    
  </div>
  <div class="sidebar-item side-item-stat right-motion">
    <div class="sidebar-item-box">
      <a href="/archives/">
        
        <span class="site-item-stat-count">23</span>
        <span class="site-item-stat-name language" data-lan="article">文章</span>
      </a>
    </div>
    <div class="sidebar-item-box">
      <a href="/tags/">
        <span class="site-item-stat-count">13</span>
        <span class="site-item-stat-name language" data-lan="tag">标签</span>
      </a>
    </div>
  </div>
  
  
    <div class="sidebar-item sidebar-item-social">
      <div class="social-item">
        
          
            <a href="https://github.com/xstarling">
              <i class="fa fa-github" title="GitHub"></i>
            </a>
          
            <a href="https://gitee.com/xstarling">
              <i class="fa fa-github-alt" title="Gitee"></i>
            </a>
          
            <a href="mailto:xstar-ling@qq.com">
              <i class="fa fa-envelope" title="E-Mail"></i>
            </a>
          
        
        
      </div>
    </div>
  



</div>
</div>
<script>
  let sidebarMeta = document.querySelector('#sidebarMeta');
  let scheme = 'mist';
  let sidebarWrapper = document.querySelector('.sidebar-wrapper');
  if (sidebarMeta && (scheme === 'pisces' || scheme === 'gemini')) {
    document.addEventListener('scroll', function(e) {
      if (document.scrollingElement.scrollTop > parseInt(sidebarMeta.style.marginTop) + 10) {
        sidebarWrapper.classList.add('home-sidebar-fixed')
      } else {
        sidebarWrapper.classList.remove('home-sidebar-fixed')
      }
    });
  }
  </script>
        <div class="section-box tag-line box-shadow-wrapper">
          <section class="section tags-section posts-expand bg-color">
            <div class="padding-wrapper">
  <div class="tag-timeline-box">
    <div class="tag-timeline-wrapper">
      <div class="tag-timeline-title">
        <h2>
          linux
          <small class="language" data-lan="tag">标签</small>
        </h2>
      </div>
      
      
      
      <a href="https://xstarling.gitee.io/9tbat_4xC/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              07-21
              <small>Linux中__init和__exit</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/lw5QlVMqW/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              07-19
              <small>基于虚拟机的驱动程序开发</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/Bgv24BHxK/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-27
              <small>嵌入式---知识盲点</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/z9c6qfcIW/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-24
              <small>Uboot源码分析</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      <a href="https://xstarling.gitee.io/7nr0vBgCD/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-22
              <small>uboot编译过程学习</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      <a href="https://xstarling.gitee.io/wq0nSn5jN/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-20
              <small>linux中VFS</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      <a href="https://xstarling.gitee.io/GTICcd6rB/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-20
              <small>基于fuse实现自定义用户态文件系统</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/imx6ull-de-uboot-he-kernel-bian-yi-qi-dong/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-09
              <small>开发板的uboot和kernel编译启动</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/linux-nei-he-yi-zhi/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              05-01
              <small>linux内核移植</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/linux-ufw/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              04-26
              <small>linux的ufw防火墙使用</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/linux_kernel_compiler/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              04-23
              <small>linux内核编译</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      <a href="https://xstarling.gitee.io/linux-partition/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              04-23
              <small>linux磁盘分区</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      <a href="https://xstarling.gitee.io/qemu/">
        <div class="motion-warpper">
          <div class="tag-post-node">
            <h1>
              04-22
              <small>QEMU安装踩坑日记</small>
            </h1>
          </div>
        </div>
      </a>
      
      
      
      
      
      
      
      
      
      
      
    </div>
  </div>
</div>
          </section>
        </div>
      </div>
    </div>
    <div class="footer-box">

<!-- 更新图片路径 -开始 -->
<script src="/media/js/fairy_updateLabelAttrs.js"></script>


<input id = "updateLabelAttrs" type="hidden" value="[{&#34;describe-use-way&#34;:&#34;替换minio图库到gitee pages图库&#34;,&#34;htmlTag&#34;:&#34;a=href|img=src&#34;,&#34;newlabelsrc&#34;:&#34;https://raw.githubusercontent.com/xstarling/images-fairy/master/blog&#34;,&#34;oldlabelsrc&#34;:&#34;https://33373sl116.zicp.fun/image-blog&#34;},{&#34;describe-use-way&#34;:&#34;替换gitee仓库路径到gitee pages图库路径&#34;,&#34;htmlTag&#34;:&#34;a=href|img=src&#34;,&#34;newlabelsrc&#34;:&#34;https://raw.githubusercontent.com/xstarling/images-fairy/master/blog/&#34;,&#34;oldlabelsrc&#34;:&#34;https://gitee.com/xstarling/images/raw/master/blog/&#34;},{&#34;describe-use-way&#34;:&#34;替换gitee pages中当前路径中/images/*.png不能访问&#34;,&#34;htmlTag&#34;:&#34;a=href|img=src&#34;,&#34;newlabelsrc&#34;:&#34;https://raw.githubusercontent.com/xstarling/images-fairy/master/blog/&#34;,&#34;oldlabelsrc&#34;:&#34;https://xstarling.gitee.io/images/&#34;},{&#34;describe-use-way&#34;:&#34;替换页面中的gitee域名为github，解决504网关问题&#34;,&#34;htmlTag&#34;:&#34;a=href&#34;,&#34;newlabelsrc&#34;:&#34;https://xstarling.github.io&#34;,&#34;oldlabelsrc&#34;:&#34;https://xstarling.gitee.io&#34;}]" >
                       

<!-- 更新图片路径 -结束 -->


  <footer class="footer">
    <center id="runTimeBox">
      已运行:<span id="run_time"></span>
    </center>
    <span id="busuanzi_container_site_pv">浏览数:<span id="busuanzi_value_site_pv"></span> 次</span>
    <span class="post-meta-divider">|</span>
    <span id="busuanzi_container_site_uv">访客数:<span id="busuanzi_value_site_uv"></span> 人</span>

    <script>
      BirthDay = new Date('未知');
      if (BirthDay.getTime()) {
        function runTime() {
          str = "";
          today = new Date();
          timeold = today.getTime() - BirthDay.getTime();
          msPerDay = 24 * 60 * 60 * 1000;
          e_daysold = timeold / msPerDay;
          daysold = Math.floor(e_daysold);
          str += daysold + "天";
          return str;
        }
        setInterval(function () {
          $("#run_time").html(runTime());
        }, 1000);
      } else {
        document.querySelector('.footer').removeChild(document.querySelector('#runTimeBox'));
      }
    </script>
    <div class="poweredby">
      Powered by <a href="https://github.com/getgridea/gridea" target="_blank">Gridea</a>
    </div>
  </footer>
  
    
      <div class="drawer-box left" id="drawer_box">
        <span class="muse-line muse-line-first"></span>
        <span class="muse-line muse-line-middle"></span>
        <span class="muse-line muse-line-last"></span>
      </div>
      
        <div class="mist back-to-top" id="back_to_top">
          <i class="fa fa-arrow-up"></i>
          
        </div>
        
                  
                        
</div>
<script>
  let sideBarOpen = "sidebar-open";
  let body = document.body;
  let back2Top = document.querySelector("#back_to_top"),
    back2TopText = document.querySelector("#back_to_top_text"),
    drawerBox = document.querySelector("#drawer_box"),
    rightSideBar = document.querySelector(".sidebar"),
    viewport = document.querySelector("body");

  function scrollAnimation(currentY, targetY) {
    let needScrollTop = targetY - currentY;
    let _currentY = currentY;
    setTimeout(() => {
      const dist = Math.ceil(needScrollTop / 10);
      _currentY += dist;
      window.scrollTo(_currentY, currentY);
      if (needScrollTop > 10 || needScrollTop < -10) {
        scrollAnimation(_currentY, targetY);
      } else {
        window.scrollTo(_currentY, targetY);
      }
    }, 1);
  }

  back2Top.addEventListener("click", function (e) {
    scrollAnimation(document.scrollingElement.scrollTop, 0);
    e.stopPropagation();
    return false;
  });

  window.addEventListener("scroll", function (e) {
    let percent =
      (document.scrollingElement.scrollTop /
        (document.scrollingElement.scrollHeight -
          document.scrollingElement.clientHeight)) *
      100;
    if (percent > 1 && !back2Top.classList.contains("back-top-active")) {
      back2Top.classList.add("back-top-active");
    }
    if (percent == 0) {
      back2Top.classList.remove("back-top-active");
    }
    if (back2TopText) {
      back2TopText.textContent = Math.floor(percent);
    }
  });

  let hasCacu = false;
  window.addEventListener("resize", function (e) {
    calcuHeight();
  });

  function calcuHeight() {
    // 动态调整站点概览高度
    if (
      (!hasCacu && back2Top.classList.contains("pisces")) ||
      back2Top.classList.contains("gemini")
    ) {
      let sideBar = document.querySelector(".sidebar");
      let navUl = document.querySelector("#site_nav");
      sideBar.style =
        "margin-top:" + (navUl.offsetHeight + navUl.offsetTop + 15) + "px;";
      hasCacu = true;
    }
  }
  calcuHeight();

  let open = false,
    MOTION_TIME = 300,
    RIGHT_MOVE_DIS = "320px";

  if (drawerBox) {
    let rightMotions = document.querySelectorAll(".right-motion");
    let right = drawerBox.classList.contains("right");

    let transitionDir = right
      ? "transition.slideRightIn"
      : "transition.slideLeftIn";

    let openProp, closeProp;
    if (right) {
      openProp = {
        paddingRight: RIGHT_MOVE_DIS,
      };
      closeProp = {
        paddingRight: "0px",
      };
    } else {
      openProp = {
        paddingLeft: RIGHT_MOVE_DIS,
      };
      closeProp = {
        paddingLeft: "0px",
      };
    }

    drawerBox.onclick = function () {
      open = !open;
      jQuery.Velocity(rightSideBar, "stop");
      jQuery.Velocity(viewport, "stop");
      jQuery.Velocity(rightMotions, "stop");
      if (open) {
        jQuery.Velocity(
          rightSideBar,
          {
            width: RIGHT_MOVE_DIS,
          },
          {
            duration: MOTION_TIME,
            begin: function () {
              jQuery.Velocity(rightMotions, transitionDir, {});
            },
          }
        );
        jQuery.Velocity(viewport, openProp, {
          duration: MOTION_TIME,
        });
      } else {
        jQuery.Velocity(
          rightSideBar,
          {
            width: "0px",
          },
          {
            duration: MOTION_TIME,
            begin: function () {
              jQuery.Velocity(rightMotions, {
                opacity: 0,
              });
            },
          }
        );
        jQuery.Velocity(viewport, closeProp, {
          duration: MOTION_TIME,
        });
      }
      for (let i = 0; i < drawerBox.children.length; i++) {
        drawerBox.children[i].classList.toggle("muse-line");
      }
      drawerBox.classList.toggle(sideBarOpen);
    };
  }

  // 链接跳转
  let newWindow = "true";
  if (newWindow === "true") {
    let links = document.querySelectorAll(".post-body a");
    links.forEach((item) => {
      if (!item.classList.contains("btn")) {
        item.setAttribute("target", "_blank");
      }
    });
  }

  let faSearch = document.querySelector("#fa_search");
  faSearch &&
    faSearch.addEventListener("click", function () {
      document.querySelector("#search_mask").style = "";
    });

  // 代码高亮
  hljs.initHighlightingOnLoad();

  // 离开当前页title变化
  var leaveTitle = "";
  var normal_title = document.title;
  if (leaveTitle) {
    document.addEventListener("visibilitychange", function () {
      if (document.visibilityState == "hidden") {
        normal_title = document.title;
        document.title = leaveTitle;
      } else {
        document.title = normal_title;
      }
    });
  }
</script>

<link rel="stylesheet" href="/media/css/jquery.fancybox.css" />
<script src="/media/js/jquery.fancybox.js"></script>

<script>
  let images = document.querySelectorAll(".section img");
  images.forEach((image) => {
    var parent = image.parentElement;
    var next = image.nextElementSibling;
    parent.removeChild(image);
    var aelem = document.createElement("a");
    aelem.href = image.src;
    aelem.dataset["fancybox"] = "images";
    aelem.dataset["rel"] = "fancybox-button";
    aelem.classList.add("fancybox");
    aelem.appendChild(image);
    parent.insertBefore(aelem, next);
  });
</script>
  </div>
</body>

  <div class="search-mask" id="search_mask" style="display: none;">
  <div class="search-box">
    <div class="search-title">
      <i class="fa fa-search"></i>
      <div class="input-box">
        <input id="search" type="text" class="language" data-lan="search" placeholder="搜索">
      </div>
      <i id="close" class="fa fa-times-circle"></i>
    </div>
    <div class="stat-box">
      <span id="stat_count">0</span><span class="language" data-lan="stat">条相关条目，使用了</span><span id="stat_times">0</span><span class="language" data-lan="stat-time">毫秒</span>
      <hr>
    </div>
    <div class="result" id="result">
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/9tbat_4xC/"" data-c="
          &lt;h1 id=&#34;1-__init和__exit的作用&#34;&gt;1. __init和__exit的作用&lt;/h1&gt;
&lt;p&gt;参考博客链接：&lt;a href=&#34;https://www.cnblogs.com/linengier/p/12380780.html&#34;&gt;linengier-linux kernel __init和__exit宏&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;会在编译的时候，将对应的代码段放入到 &lt;code&gt;.init.text&lt;/code&gt;段内存中，起一个标识作用&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;2-源码剖析&#34;&gt;2. 源码剖析&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;linux4.14/include/linux/init.h&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/* These are for everybody (although not all archs will actually
   discard it in modules) */
#define __init		__section(.init.text) __cold __inittrace __latent_entropy __noinitretpoline
#define __initdata	__section(.init.data)
#define __initconst	__section(.init.rodata)
#define __exitdata	__section(.exit.data)
#define __exit_call	__used __section(.exitcall.exit)
&lt;/code&gt;&lt;/pre&gt;
">Linux中__init和__exit</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/lw5QlVMqW/"" data-c="
          &lt;h1 id=&#34;1-问题汇总&#34;&gt;1. 问题汇总&lt;/h1&gt;
&lt;h3 id=&#34;11-把编译好的ko文件加载模块时出错error-could-not-insert-module-hello_worldko-invalid-module-format&#34;&gt;1.1 把编译好的.ko文件加载模块时出错：Error: could not insert module hello_world.ko: Invalid module format&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;编译.ko文件的内核源码和工具链，与目标源码的内核源码和运行环境不匹配&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;12-nfs文件系统搭建好传输文件报错touch-cannot-touch-testc-read-only-file-system&#34;&gt;1.2 nfs文件系统搭建好，传输文件报错：touch: cannot touch `test.c&#39;: Read-only file system&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;mount -o remount rw /      ：检查文件系统是否挂载正常&lt;/li&gt;
&lt;li&gt;检查 &lt;code&gt;/etc/exports&lt;/code&gt; 文件配置是否正确，nfs-server释放正常启动&lt;/li&gt;
&lt;li&gt;service nfs-server start/status/stop&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;13-export_symbolvar导出static修饰变量报错homelearnsoftwareworksdriversmodule_symbolmodule_paramo-error-local-symbol-itype-was-exported&#34;&gt;1.3 EXPORT_SYMBOL(var)：导出static修饰变量报错：/home/learn/software/works/drivers/module_symbol/module_param.o: error: local symbol &#39;itype&#39; was exported&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;取消对应变量的static关键字&lt;br&gt;
&lt;a href=&#34;https://zhuanlan.zhihu.com/p/27586298&#34;&gt;static、extern关键字说明&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;2-hello驱动加载学习&#34;&gt;2. hello驱动加载学习&lt;/h1&gt;
&lt;h3 id=&#34;21-实验环境&#34;&gt;2.1 实验环境&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;开发版烧录号ubuntu/Debian镜像&lt;/li&gt;
&lt;li&gt;启动开发版，搭建好nfs客户端，挂载共享文件夹&lt;/li&gt;
&lt;li&gt;获取Debian镜像的内核源码并编译&lt;/li&gt;
&lt;li&gt;下载交叉编译工具链&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;22-编译41971版本内核&#34;&gt;2.2 编译4.19.71版本内核&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2.2.1 下载镜像源文件&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.2.2 安装交叉编译工具链&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.2.3 编译内核设置编译路径&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;deb_distro=bionic&lt;/p&gt;
&lt;p&gt;DISTRO=stable&lt;/p&gt;
&lt;p&gt;build_opts=&amp;quot;${build_opts} O=/home/pi/build&amp;quot;&lt;/p&gt;
&lt;p&gt;make ${build_opts} npi_v7_defonfig&lt;/p&gt;
&lt;p&gt;make ${build_opts}&lt;/p&gt;
&lt;p&gt;make ${build_opts} bindeb-pkg&lt;/p&gt;
&lt;h3 id=&#34;23-内核模块头文件&#34;&gt;2.3 内核模块头文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;#include&amp;lt;linux/module.h&amp;gt;：包函内核模块信息声明的相关函数&lt;/li&gt;
&lt;li&gt;#include&amp;lt;linux/init.h&amp;gt;：包含了module_init()和module_exit()函数的&lt;/li&gt;
&lt;li&gt;#include&amp;lt;linux/kernel.h&amp;gt;：包含内核提供的各种函数，如printk()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;24-内核模块打印函数&#34;&gt;2.4 内核模块打印函数&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;printf：glibc实现的打印函数，工作与用户空间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;printk：内核模块无法使用glibc库函数，内核自身实现的一个类printf函数，但是需要指定打印等级。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;#define KERN_EMEGR    &amp;quot;&amp;lt;0&amp;gt;&amp;quot;通常是系统崩溃前的信息&lt;/li&gt;
&lt;li&gt;#define KERN_ALERT    &amp;quot;&amp;lt;1&amp;gt;&amp;quot;需要立即处理的消息&lt;/li&gt;
&lt;li&gt;#define KERN_CRIT    &amp;quot;&amp;lt;2&amp;gt;&amp;quot;严重情况&lt;/li&gt;
&lt;li&gt;#define KERN_ERR    &amp;quot;&amp;lt;3&amp;gt;&amp;quot;错误情况&lt;/li&gt;
&lt;li&gt;#define KERN_WARNING    &amp;quot;&amp;lt;4&amp;gt;&amp;quot;有问题的情况&lt;/li&gt;
&lt;li&gt;#define KERN_NOTICE    &amp;quot;&amp;lt;5&amp;gt;&amp;quot;注意信息&lt;/li&gt;
&lt;li&gt;#define KERN_EMEGR    &amp;quot;&amp;lt;6&amp;gt;&amp;quot;普通信息&lt;/li&gt;
&lt;li&gt;#define KERN_EMEGR    &amp;quot;&amp;lt;7&amp;gt;&amp;quot;调试信息&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;查看当前系统printk打印等级&lt;/strong&gt;：cat /proc/sys/kernel/printk&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当前控制台日志级别（第一数字）&lt;/li&gt;
&lt;li&gt;默认消息日志级别（第二数字）&lt;/li&gt;
&lt;li&gt;最小的控制台级别（第三数字）&lt;/li&gt;
&lt;li&gt;默认控制台日志级别（第四数字）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;//使用系统默认打印级别———（默认的打印级别 &amp;lt; 当前打印级别）不能打印&lt;br&gt;
printk(&amp;quot;[default] Hello World Module init\n&amp;quot;);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;打印内核所有打印信息&lt;/strong&gt;：dmesg&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内核log缓冲区大小有限制，缓冲区数据可能被冲掉&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;25-makefile分析&#34;&gt;2.5 Makefile分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;KERNEL_DIR：指向Linux内核具体路径&lt;/li&gt;
&lt;li&gt;export：导出变量给子Makefile使用&lt;/li&gt;
&lt;li&gt;obj-m := &amp;lt;模块名&amp;gt;.o ：定义要生成的模块&lt;/li&gt;
&lt;li&gt;$(MAKE)：Makefile的默认变量，值为make&lt;/li&gt;
&lt;li&gt;选型“-C”：让make工具跳转到Linux内核目录下读取顶层Makefile&lt;/li&gt;
&lt;li&gt;M=：表示内核模块源码目录&lt;/li&gt;
&lt;li&gt;$(CURDIR)：Makefile默认变量，值为当前目录所在路径&lt;/li&gt;
&lt;li&gt;make modules：执行Linux顶层Makefile的伪目标，它实现内核模块的源码读取并且编译为#.ko#文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;25-编译内核模块&#34;&gt;2.5 编译内核模块&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;make
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;26-把生成的内核模块拷贝到nfs共享目录&#34;&gt;2.6 把生成的内核模块拷贝到nfs共享目录&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;make copy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;27-开发版加载-卸载及查看内核模块&#34;&gt;2.7 开发版加载、卸载及查看内核模块&lt;/h3&gt;
&lt;p&gt;insmod xxx.ko	//加载内核模块&lt;/p&gt;
&lt;p&gt;lsmod | grep &amp;quot;hello&amp;quot;			//查看内核模块&lt;/p&gt;
&lt;p&gt;rmmod			//卸载内核模块&lt;/p&gt;
&lt;h3 id=&#34;28-代码&#34;&gt;2.8 代码&lt;/h3&gt;
&lt;h4 id=&#34;281-c代码&#34;&gt;2.8.1 c代码&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include&amp;lt;linux/module.h&amp;gt;
#include&amp;lt;linux/init.h&amp;gt;
#include&amp;lt;linux/kernel.h&amp;gt;

/*
*	__init：是一个空的宏定义，用于标识当前函数为初始化函数
*/
static int __init hello_init(void){
	//设置最高级别打印信息
	printk(KERN_EMERG &amp;quot;[KERN_EMERG] Hello World Module init\n&amp;quot;);
	//使用系统默认打印级别———（默认的打印级别 &amp;lt; 当前打印级别）不能打印
	printk(&amp;quot;[default] Hello World Module init\n&amp;quot;);
	return 0;
}

static void __exit hello_exit(void){
	printk(&amp;quot;[default] good bye\n&amp;quot;);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE(&amp;quot;GPL2&amp;quot;);
MODULE_AUTHOR(&amp;quot;xlx&amp;quot;);
MODULE_DESCRIPTION(&amp;quot;Welcome Ling driver&amp;quot;);
MODULE_ALIAS(&amp;quot;Ling:hello&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;282-make代码&#34;&gt;2.8.2 make代码&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;# 内核源码路径
#KERNEL_DIR = /home/learn/software/kernel/linux-5.19/
KERNEL_DIR = /usr/src/linux-headers-5.19.0-46-generic/

#ARCH=x86
#CROSS_COMPILE=
#export ARCH CROSS_COMPILE

obj-m := hello.o
all:
        $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
    
.PHONE:clean copy

clean:
        $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean

copy:
        sudo cp *.ko /home/learn/software/kernel/linux-5.19/drivers/ling

&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;3-内核模块和符号共享&#34;&gt;3. 内核模块和符号共享&lt;/h1&gt;
&lt;h3 id=&#34;mobaxterm设置窗口行数列数&#34;&gt;mobaxterm设置窗口行数，列数&lt;/h3&gt;
&lt;p&gt;liux终端窗口&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查看终端窗口的行列：stty size&lt;/li&gt;
&lt;li&gt;设置终端窗口列数：stty cols xxx&lt;/li&gt;
&lt;li&gt;设置终端窗口行数：stty rows xxx&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;mobaxterm窗口&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;右击会话窗口，选择“edit session” --- “Terminal settings” --- “Terminal font settings” --- “Terminal size”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;模块参数&#34;&gt;模块参数&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1C64y1z7WL/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;[Linux]内核模块参数和导出符号，增强你的内核模块功能_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据不同应用场合给内核模块传递不同参数，提高内核模块灵活性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;定义一个常见变量&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用module_param宏把传参值赋给变量&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;module_param（name，type，perm）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;name：参数名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;type：参数类型，如：int，byte（对应char），bool，charp（对应char*）......&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;perm：读写权限&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不允许设置可执行权限&lt;/li&gt;
&lt;li&gt;在“sys/module/模块名/parameters”目录下，会生成该参数对应的文件名（perm=0，则不会生成参数对应的文件名）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;符号共享&#34;&gt;符号共享&lt;/h3&gt;
&lt;p&gt;内核模块可以共享导出的符号表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;变量共享&lt;/li&gt;
&lt;li&gt;函数共享&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;EXPORT_SYMBOL(sym)
//sym：变量名或函数名
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看符号表&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;cat /proc/kallsyms | grep xxx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Makefile修改&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有依赖关系的模块，放在一起进行编译&lt;/li&gt;
&lt;li&gt;clean伪目标增加清除共享文件夹指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模块手动加载&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;加载是，必须先加载相关依赖模块&lt;/li&gt;
&lt;li&gt;卸载时，顺序想法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模块自动加载&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;所有内核模块统一放到“/lib/modules/内核版本”目录下&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;cp *.ko /lib/modules/内核版本	//此目录用来存放当前操作系统的.ko文件
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;建立模块依赖关系&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;depmod -a
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看模块依赖关系&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;cat /lib/modules/内核版本/modules.dep
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;加载模块及其他依赖模块&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;modprobe xxx
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;卸载模块及其依赖模块&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;modprobe -r xxx
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;附件代码示例&#34;&gt;附件代码示例&lt;/h3&gt;
&lt;h4 id=&#34;module_paramc代码&#34;&gt;module_param.c代码&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include&amp;lt;linux/module.h&amp;gt;
#include&amp;lt;linux/init.h&amp;gt;
#include&amp;lt;linux/kernel.h&amp;gt;

static int itype = 0;
module_param(itype, int, 0);

static bool btype = 0;
module_param(btype, bool, 0644);

static char ctype = 0;
module_param(ctype, byte, 0);

static char* stype = 0;
module_param(stype, charp, 0644);

static int __init param_init(void){
	printk(KERN_ALERT &amp;quot;param init\n&amp;quot;);
	printk(KERN_ALERT &amp;quot;itype=%d\n&amp;quot;,itype);
	printk(KERN_ALERT &amp;quot;btype=%d\n&amp;quot;,btype);
	printk(KERN_ALERT &amp;quot;ctype=%d\n&amp;quot;,ctype);
	printk(KERN_ALERT &amp;quot;stype=%s\n&amp;quot;,stype);
	return 0;
}

static void __exit param_exit(void){
	printk(KERN_ALERT &amp;quot;module exit\n&amp;quot;);
}

EXPORT_SYMBOL(itype);

int my_add(int a,int b){
	return a + b;
}

EXPORT_SYMBOL(my_add);

int my_sub(int a,int b){
	return a - b;
}

EXPORT_SYMBOL(my_sub);

module_init(param_init);
module_exit(param_exit);

MODULE_LICENSE(&amp;quot;GPL2&amp;quot;);
MODULE_AUTHOR(&amp;quot;xlx&amp;quot;);
MODULE_DESCRIPTION(&amp;quot;module_param&amp;quot;);
MODULE_ALIAS(&amp;quot;Ling:module_param&amp;quot;);

&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;calculationh代码&#34;&gt;calculation.h代码&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#ifndef __CALCULATION_H__
#define __CALCULATION_H__

extern int itype;

int my_add(int a,int b);
int my_sub(int a,int b);

#endif

&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;calculationc代码&#34;&gt;calculation.c代码&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include&amp;lt;linux/module.h&amp;gt;
#include&amp;lt;linux/init.h&amp;gt;
#include&amp;lt;linux/kernel.h&amp;gt;

#include &amp;quot;calculation.h&amp;quot;

static int __init calculation_init(void){
	printk(KERN_ALERT &amp;quot;calculation init\n&amp;quot;);
	printk(KERN_ALERT &amp;quot;itype + 1=%d,itype - 1=%d\n&amp;quot;,my_add(itype, 1),my_sub(itype, 1));
	return 0;
}

static void __exit calculation_exit(void){
	printk(KERN_ALERT &amp;quot;calculation exit\n&amp;quot;);
}

module_init(calculation_init);
module_exit(calculation_exit);


MODULE_LICENSE(&amp;quot;GPL2&amp;quot;);
MODULE_AUTHOR(&amp;quot;xlx&amp;quot;);
MODULE_DESCRIPTION(&amp;quot;calculation module&amp;quot;);
MODULE_ALIAS(&amp;quot;Ling:calculation module&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;4-字符设备&#34;&gt;4. 字符设备&lt;/h1&gt;
&lt;p&gt;copy&lt;/p&gt;
">基于虚拟机的驱动程序开发</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/BRVneXEB3/"" data-c="
          &lt;h1 id=&#34;1-前期知识准备&#34;&gt;1. 前期知识准备&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;555定时器&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1fr4y1r7Tk/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-555定时器&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;比较器和RS触发器&lt;br&gt;
比较器：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061352535.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
RS触发器：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061354076.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061355695.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;原理图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306052325437.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061400832.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;工作模式&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;无稳态电路&lt;br&gt;
单稳态电路&lt;br&gt;
双稳态电流&lt;br&gt;
1. 基本无稳态电路&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060903849.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060923462.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
2. 任意占空比的无稳态电路&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060929125.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060931983.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
3. 50%占空比无稳态电路&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060933131.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060934400.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;注意&lt;/strong&gt;：&lt;br&gt;
应用此电路时要注意，电阻R1不能太小，否则会影响电容C1的充电，导致占空比不是50%。&lt;br&gt;
4. 单稳态模式&lt;br&gt;
在单稳态电路中，输出端保持低电平，在输入端触发之后，输出端变成高电平，延时一段时间后回到低电平并保持。这个特性可以用来制作延时开关。原理图如下：&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060938425.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061335617.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
1. 双稳态模式&lt;br&gt;
双稳态电路指的是输出端可以稳定在高电平或低电平两种状态，可以通过开关在两个状态间切换。这个特性可以用来制作按钮开关。&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061336661.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061340725.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;滤波原理&lt;/strong&gt;：&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1ri4y1y7yG/?spm_id_from=333.999.0.0&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-滤波器&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;高通滤波器&lt;br&gt;
https://zhuanlan.zhihu.com/p/347963026
&lt;ul&gt;
&lt;li&gt;电路图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306161448043.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;低通滤波器&lt;br&gt;
https://zhuanlan.zhihu.com/p/347963026
&lt;ul&gt;
&lt;li&gt;电路图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306161426110.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;电路容抗&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306161436378.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;带通滤波器&lt;br&gt;
https://zhuanlan.zhihu.com/p/347963026&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;参考博客链接：&lt;a href=&#34;https://blog.csdn.net/bigbearger/article/details/104573802&#34;&gt;bigbearger-带通滤波器&lt;/a&gt;&lt;br&gt;
可以由低通滤波和高通滤波进行组合&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;差分比例运放电路&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062106283.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
差分电路改进：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062111183.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;积分电路设计&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/52899137&#34;&gt;积分，微分电路&lt;/a&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV17y4y1R7zX/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;电子科技队长&lt;/a&gt;&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062017211.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062020616.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
积分电路应用：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同向积分电路&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062124150.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;微分电路设计&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/52899137&#34;&gt;积分，微分电路&lt;/a&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV16A411m7Su/?spm_id_from=pageDriver&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;微分电路&lt;/a&gt;&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062154378.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062156256.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运放和比较器的区别&lt;br&gt;
参考链接：&lt;a href=&#34;https://www.bilibili.com/read/cv9412435&#34;&gt;运算放大器和比较器区别&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
">基于555定时器的波形转换</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/7lHveweih/"" data-c="
          &lt;h1 id=&#34;1-电路设计&#34;&gt;1. 电路设计&lt;/h1&gt;
&lt;h1 id=&#34;2-电感排列&#34;&gt;2. 电感排列&lt;/h1&gt;
&lt;h1 id=&#34;3-归一化&#34;&gt;3. 归一化&lt;/h1&gt;
&lt;h1 id=&#34;4-如何平稳运行代码&#34;&gt;4. 如何平稳运行（代码）&lt;/h1&gt;
&lt;h1 id=&#34;5-环岛&#34;&gt;5. 环岛&lt;/h1&gt;
">智能车（电磁循迹）</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/0MnwBOxFh/"" data-c="
          &lt;h1 id=&#34;1-vscode阅读源码前提条件&#34;&gt;1. vscode阅读源码前提条件&lt;/h1&gt;
&lt;p&gt;参考博客链接：&lt;a href=&#34;https://blog.csdn.net/weixin_40209493/article/details/128175003&#34;&gt;Engineer-Jaylen_Sun-VScode+clangd阅读linux内核源码&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clangd插件---安装&lt;/li&gt;
&lt;li&gt;禁止c/c++ intellisense：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306102321760.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;源码目录下有compile_commands.json&lt;/li&gt;
&lt;li&gt;compile_commands.json里面含有文件，并且修改&lt;code&gt;&amp;quot;cc&amp;quot;&lt;/code&gt; 为 &lt;code&gt;&amp;quot;xxx-gcc&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;make clean
bear make ......
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;2-vscode快捷键使用&#34;&gt;2. vscode快捷键使用&lt;/h1&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/qq_45261963/article/details/108695261&#34;&gt;昱Wy-vscode快捷键&lt;/a&gt;&lt;/p&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;1&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306102334075.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
">vscode阅读源码</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/KwiVAQPlb/"" data-c="
          &lt;h2 id=&#34;1-gdb基本命令&#34;&gt;1. gdb基本命令&lt;/h2&gt;
&lt;h3 id=&#34;11-gdb安装&#34;&gt;1.1 gdb安装&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;安装命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo apt install gdb
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;注意&lt;br&gt;
注意，如果服务器没有安装gdb，上面命令自动安装最新的gdb。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-gdb调试准备&#34;&gt;1.2 gdb调试准备&lt;/h3&gt;
&lt;p&gt;用gcc编译源程序的时候，编译后的可执行文件&lt;strong&gt;默认不包括源程序代码&lt;/strong&gt;，如果你打算编译后的程序可以被调试，编译的时候要加&lt;code&gt;-g&lt;/code&gt;的参数。&lt;br&gt;
例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;gcc -g -o test test.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在命令行输入如下命令，即可进入gdb调试&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;gdb test
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;13-基本调试命令&#34;&gt;1.3 基本调试命令&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令名称&lt;/th&gt;
&lt;th&gt;命令缩写&lt;/th&gt;
&lt;th&gt;命令说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;run&lt;/td&gt;
&lt;td&gt;r&lt;/td&gt;
&lt;td&gt;运行一个待调试的程序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;continue&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;让暂停的程序继续运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;next&lt;/td&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;运行到下一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;step&lt;/td&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;单步执行，遇到函数会进入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;until&lt;/td&gt;
&lt;td&gt;u&lt;/td&gt;
&lt;td&gt;运行到指定行停下来&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;finish&lt;/td&gt;
&lt;td&gt;fi&lt;/td&gt;
&lt;td&gt;结束当前调用函数，回到上一层调用函数处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;return&lt;/td&gt;
&lt;td&gt;return&lt;/td&gt;
&lt;td&gt;结束当前调用函数并返回指定值，到上一层函数调用处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jump&lt;/td&gt;
&lt;td&gt;j&lt;/td&gt;
&lt;td&gt;将当前程序执行流跳转到指定行或地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;print&lt;/td&gt;
&lt;td&gt;p&lt;/td&gt;
&lt;td&gt;打印变量或寄存器值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backtrace&lt;/td&gt;
&lt;td&gt;bt&lt;/td&gt;
&lt;td&gt;查看当前线程的调用堆栈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frame&lt;/td&gt;
&lt;td&gt;f&lt;/td&gt;
&lt;td&gt;切换到当前调用线程的指定堆栈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;thread&lt;/td&gt;
&lt;td&gt;thread&lt;/td&gt;
&lt;td&gt;切换到指定线程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;break&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;添加断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tbreak&lt;/td&gt;
&lt;td&gt;tb&lt;/td&gt;
&lt;td&gt;添加临时断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delete&lt;/td&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;删除断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;enable&lt;/td&gt;
&lt;td&gt;enable&lt;/td&gt;
&lt;td&gt;启用某个断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;disable&lt;/td&gt;
&lt;td&gt;disable&lt;/td&gt;
&lt;td&gt;禁用某个断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;watch&lt;/td&gt;
&lt;td&gt;watch&lt;/td&gt;
&lt;td&gt;监视某一个变量或内存地址的值是否发生变化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;list&lt;/td&gt;
&lt;td&gt;l&lt;/td&gt;
&lt;td&gt;显示源码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;info&lt;/td&gt;
&lt;td&gt;i&lt;/td&gt;
&lt;td&gt;查看断点 / 线程等信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ptype&lt;/td&gt;
&lt;td&gt;ptype&lt;/td&gt;
&lt;td&gt;查看变量类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;disassemble&lt;/td&gt;
&lt;td&gt;dis&lt;/td&gt;
&lt;td&gt;查看汇编代码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set args&lt;/td&gt;
&lt;td&gt;set args&lt;/td&gt;
&lt;td&gt;设置程序启动命令行参数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show args&lt;/td&gt;
&lt;td&gt;show args&lt;/td&gt;
&lt;td&gt;查看设置的命令行参数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;2-调试core文件&#34;&gt;2. 调试core文件&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;当程序挂掉时，系统缺省不会生成core文件。&lt;br&gt;
core文件：有问题的程序运行后，产生“段错误 (核心已转储)”时生成的具有堆栈信息和调试信息的文件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;配置core文件&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ulimit -a 查看系统参数；&lt;/li&gt;
&lt;li&gt;ulimit -c 查看core开关，如果为0表示关闭，不会生成core文件；&lt;/li&gt;
&lt;li&gt;ulimit -c [filesize]  设置core文件大小，当最小设置为4之后才会生成core文件&lt;/li&gt;
&lt;li&gt;ulimit -c unlimit 把core文件的大小设置为无限制；&lt;/li&gt;
&lt;li&gt;运行程序，生成core文件；&lt;/li&gt;
&lt;li&gt;gdb 程序名 core文件名。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;core文件命名和保存路径&lt;br&gt;
①core文件有默认的名称和路径，但为了方便，我们通常会自己命名和指定保存路径；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;②可以通过 &lt;code&gt;/proc/sys/kernel/core_pattern&lt;/code&gt; 设置 core 文件名和保存路径，方法如下：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo &amp;quot;/corefile/core-%e-%p-%t&amp;quot; &amp;gt; /proc/sys/kernel/core_pattern&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;命名的参数列表：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;     %p - insert pid into filename 添加pid 
     %u - insert current uid into filename 添加当前uid 
     %g - insert current gid into filename 添加当前gid 
     %s - insert signal that caused the coredump into the filename 添加导致产生core的信号 
     %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间 
     %h - insert hostname where the coredump happened into filename 添加主机名 
     %e - insert coredumping executable name into filename 添加命令名
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;调试core文件
&lt;ol&gt;
&lt;li&gt;方法1: gdb [exec file] [core file] 然后执行bt看堆栈信息&lt;/li&gt;
&lt;li&gt;方法2:gdb -c [core file],然后 file [exec file],最后再使用&lt;code&gt;bt&lt;/code&gt;查看错误位置&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;code&gt;bt&lt;/code&gt;：命令查看程序运行的栈&lt;/p&gt;
&lt;h2 id=&#34;3-调试正在运行中的程序&#34;&gt;3. 调试正在运行中的程序&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;调试的流程&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;ps查看程序的进程ID&lt;br&gt;
&lt;code&gt;ps -aux| grep test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;启动gdb调试程序&lt;br&gt;
&lt;strong&gt;方式0&lt;/strong&gt;：之间通过程序名和pid&lt;br&gt;
&lt;code&gt; gdb 程序名称 -p 进程ID&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;方式1&lt;/strong&gt;：通过–-pid参数来绑定指定的进程程序。&lt;br&gt;
&lt;code&gt; gdb --pid 25552&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;方式2&lt;/strong&gt;：通过程序和进程号来绑定。&lt;br&gt;
&lt;code&gt; gdb test 25552&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;方式3&lt;/strong&gt;：先启动gdb后，通过attach来绑定pid&lt;br&gt;
&lt;code&gt; gdb attach 25552&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;查看程序的栈信息&lt;br&gt;
&lt;code&gt;bt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用gdb的普通调试命令进行程序调试&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;调试命令 (缩写) 作用&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;(gdb) break (b) 在源代码指定的某一行设置断点，其中xxx用于指定具体打断点位置
(gdb) run (r） 执行被调试的程序，其会自动在第一个断点处暂停执行。
(gdb) continue (c） 当程序在某一断点处停止后，用该指令可以继续执行，直至遇到断点或者程序结束。
(gdb) next (n) 令程序一行代码一行代码的执行。
(gdb) step（s） 如果有调用函数，进入调用的函数内部；否则，和 next 命令的功能一样。
(gdb) until (u)
(gdb) until (u) location 当你厌倦了在一个循环体内单步跟踪时，单纯使用 until 命令，可以运行程序直到退出循环体。
until n 命令中，n 为某一行代码的行号，该命令会使程序运行至第 n 行代码处停止。
(gdb) print (p） 打印指定变量的值，其中 xxx 指的就是某一变量名。
(gdb) list (l) 显示源程序代码的内容，包括各行代码所在的行号。
(gdb) finish（fi） 结束当前正在执行的函数，并在跳出函数后暂停程序的执行。
(gdb) return（return） 结束当前调用函数并返回指定值，到上一层函数调用处停止程序执行。
(gdb) jump（j) 使程序从当前要执行的代码处，直接跳转到指定位置处继续执行后续的代码。
(gdb) quit (q) 终止调试。
(gdb) Backtrace(bt) 查看堆栈。
(gdb) info threads 显示当前可调试的所有线程
(gdb) thread ID 切换当前调试的线程为指定ID的线程
(gdb) attach process-id 在gdb状态下，开始调试一个正在运行的进程
(gdb) thread apply all command 所有线程执行command

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;4-调试多进程服务程序&#34;&gt;4. 调试多进程服务程序&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;调试父进程：set follow-fork-mode parent （缺省）&lt;br&gt;
调试子进程：set follow-fork-mode child&lt;br&gt;
设置调试模式：set detach-on-fork [on|off]，缺省是on&lt;br&gt;
标识调试当前进程的时候，其他的进程继续运行，如果用off调试当前进程的时候，其他的进程被gdb挂起&lt;br&gt;
查看调试的进程：info inferiors    （*：表示当前调试进程）&lt;br&gt;
切换当前调试的进程：inferior  进程ID&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;常用命令表格&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;follow-fork-mode&lt;/td&gt;
&lt;td&gt;set follow-fork-mode [parent|child]&lt;br&gt; parent：gdb默认调试的是父进程，如果参数是parent则fork之后继续调试父进程，子进程不受影响 &lt;br&gt; child：如果想调试子进程，则修改参数为child，set follow-fork-mode child之后调试子进程，父进程不受影响。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show follow-fork-mode&lt;/td&gt;
&lt;td&gt;查看当前调试的fork进程的模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;detach-on-fork&lt;/td&gt;
&lt;td&gt;该参数表明gdb在fork之后是否断开(detach)某个进程的调试，或者交给gdb控制.&lt;br&gt;set detach-on-fork [on|off]&lt;br&gt; on：断开调试follow-fork-mode调试的指定进程&lt;br&gt; off：gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试，另一个进程置于暂停（suspended）状态。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show detach-on-fork&lt;/td&gt;
&lt;td&gt;查看detach-on-fork模式&lt;br&gt; gdb将每一个被调试进程的执行状态记录在一个名为inferior的结构中。一般情况下一个inferior对应一个进程，每个不同的inferior有不同的地址空间。inferior有时候会在进程没有启动的时候就存在。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;info inferiors&lt;/td&gt;
&lt;td&gt;查询正在调试的进程,gdb会为他们分配唯一的Num号，其中前面带&#39;*&#39;号的就是正在调试的进程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inferior &lt;inferior num&gt;&lt;/td&gt;
&lt;td&gt;切换调试的进程为inferior num的进程处&lt;br&gt; add-inferior [-copies n] [-exec executable] &lt;br&gt;添加n个新的调试进程，可以用file executable来分配给inferior可执行文件。如果不指定n，则只增加一个inferior；如果不指定executable，则执行程序留空，增加后可使用file命令重新指定执行程序；这时候创建的inferior其关联的进程并没启动。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clone-inferior [-copies n] [infno]&lt;/td&gt;
&lt;td&gt;复制n个编号是infno的inferior。如果不指定n的话，就只复制一个inferior；如果不指定infno，则就复制正在调试的inferior。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;detach inferior infno&lt;/td&gt;
&lt;td&gt;断开(detach)掉编号是infno的inferior。值得注意的是这个inferior还存在，可以再次用run命令执行它。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kill inferior infno&lt;/td&gt;
&lt;td&gt;kill掉infno号inferior。值得注意的是这个inferior仍然存在，可以再次用run等命令执行它。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;remove-inferior infno&lt;/td&gt;
&lt;td&gt;删除一个infno号的inferior。删除前需要先kill或者detach这个inferior，因为当一个inferior正在运行时不能被删除.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set schedule-multiple [on|off]&lt;/td&gt;
&lt;td&gt;off：只有当前inferior会执行。 &lt;br&gt; on：全部是执行状态的inferior都会执行。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show schedule-multiple&lt;/td&gt;
&lt;td&gt;查看schedule-multiple的状态。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set follow-exec-mode [new|same]&lt;/td&gt;
&lt;td&gt;same：当发生exec的时候，在执行exec的inferior上控制子进程。 &lt;br&gt; new：新建一个inferior给执行起来的子进程。而父进程的inferior仍然保留，当前保留的inferior的程序状态是没有执行。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show follow-exec-mode&lt;/td&gt;
&lt;td&gt;查看follow-exec-mode设置的模式。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set print inferior-events [on|off]&lt;/td&gt;
&lt;td&gt;用来打开和关闭inferior状态的提示信息。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show print inferior-events&lt;/td&gt;
&lt;td&gt;查看print inferior-events设置的状态。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maint info program-spaces&lt;/td&gt;
&lt;td&gt;用来显示当前gdb管理的地址空间的数目。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;5-调试多线程服务程序&#34;&gt;5. 调试多线程服务程序&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;查看线程状态&lt;br&gt;
&lt;strong&gt;在shell中执行&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;查看当前运行的进程：ps aux | grep 进程名称&lt;br&gt;
查看当前运行的轻量级进程：ps -aL | grep 进程名称&lt;br&gt;
查看主线程和子线程的关系：pstree -p 主线程ID&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;在gdb中执行&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;查看线程：info threads&lt;br&gt;
切换线程：thread 线程ID&lt;br&gt;
只运行当前线程：set scheduler-locking on&lt;br&gt;
运行全部的线程：set scheduler-locking off&lt;br&gt;
指定某线程执行某gdb命令：thread apply 线程ID cmd命令&lt;br&gt;
全部的线程执行某gdb命令：thread apply all cmd命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;多线程调试命令&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;info threads&lt;/td&gt;
&lt;td&gt;显示当前可调试的所有线程，每个线程会有一个GDB为其分配的ID，后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;thread ID(1,2,3…)&lt;/td&gt;
&lt;td&gt;切换当前调试的线程为指定ID的线程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;break thread_test.c:123 thread all&lt;br&gt;（例：在相应函数的位置设置断点break pthread_run1）&lt;/td&gt;
&lt;td&gt;在所有线程中相应的行上设置断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;thread apply ID1 ID2 command&lt;/td&gt;
&lt;td&gt;让一个或者多个线程执行GDB命令command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;thread apply all command&lt;/td&gt;
&lt;td&gt;让所有被调试线程执行GDB命令command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set scheduler-locking 选项 command&lt;/td&gt;
&lt;td&gt;设置线程是以什么方式来执行命令&lt;br&gt;&lt;strong&gt;set scheduler-locking off&lt;/strong&gt;: 不锁定任何线程，也就是所有线程都执行，这是默认值&lt;br&gt;&lt;strong&gt;set scheduler-locking on&lt;/strong&gt;：只有当前被调试程序会执行&lt;br&gt;&lt;strong&gt;set scheduler-locking on step&lt;/strong&gt;：在单步的时候，除了next过一个函数的情况(熟悉情况的人可能知道，这其实是一个设置断点然后continue的行为)以外，只有当前线程会执行&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;6-服务程序运行日志&#34;&gt;6. 服务程序运行日志&lt;/h2&gt;
&lt;p&gt;freecplus开源框的日志工具：&lt;a href=&#34;https://xie.infoq.cn/article/3c7b99b5c8b5663c61cc06204&#34;&gt;freecplus-参考文档&lt;/a&gt;&lt;/p&gt;
">gdb调试</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/VwNstCxrN/"" data-c="
          &lt;h1 id=&#34;1-linux驱动uboot内核移植&#34;&gt;1. linux驱动，uboot内核移植&lt;/h1&gt;
&lt;h2 id=&#34;11-项目准备&#34;&gt;1.1 项目准备&lt;/h2&gt;
&lt;p&gt;参考准备：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/384128160&#34;&gt;新爷​—嵌入式设置简历准备&lt;/a&gt;&lt;br&gt;
参考准备：&lt;a href=&#34;https://www.zhihu.com/question/434468953&#34;&gt;仲一​---有面试题&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;111-波形转换555芯片&#34;&gt;1.1.1 波形转换(555芯片)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;555定时器&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1fr4y1r7Tk/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-555定时器&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;比较器和RS触发器&lt;br&gt;
比较器：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061352535.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
RS触发器：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061354076.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061355695.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;原理图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306052325437.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061400832.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;工作模式&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;无稳态电路&lt;br&gt;
单稳态电路&lt;br&gt;
双稳态电流&lt;br&gt;
1. 基本无稳态电路&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060903849.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060923462.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
2. 任意占空比的无稳态电路&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060929125.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060931983.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
3. 50%占空比无稳态电路&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060933131.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060934400.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;注意&lt;/strong&gt;：&lt;br&gt;
应用此电路时要注意，电阻R1不能太小，否则会影响电容C1的充电，导致占空比不是50%。&lt;br&gt;
4. 单稳态模式&lt;br&gt;
在单稳态电路中，输出端保持低电平，在输入端触发之后，输出端变成高电平，延时一段时间后回到低电平并保持。这个特性可以用来制作延时开关。原理图如下：&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306060938425.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061335617.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
1. 双稳态模式&lt;br&gt;
双稳态电路指的是输出端可以稳定在高电平或低电平两种状态，可以通过开关在两个状态间切换。这个特性可以用来制作按钮开关。&lt;br&gt;
电路图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061336661.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
公式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306061340725.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;滤波原理&lt;/strong&gt;：&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1ri4y1y7yG/?spm_id_from=333.999.0.0&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-滤波器&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;高通滤波器&lt;br&gt;
https://zhuanlan.zhihu.com/p/347963026
&lt;ul&gt;
&lt;li&gt;电路图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306161448043.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;低通滤波器&lt;br&gt;
https://zhuanlan.zhihu.com/p/347963026
&lt;ul&gt;
&lt;li&gt;电路图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306161426110.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;电路容抗&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306161436378.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;带通滤波器&lt;br&gt;
https://zhuanlan.zhihu.com/p/347963026&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;参考博客链接：&lt;a href=&#34;https://blog.csdn.net/bigbearger/article/details/104573802&#34;&gt;bigbearger-带通滤波器&lt;/a&gt;&lt;br&gt;
可以由低通滤波和高通滤波进行组合&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;差分比例运放电路&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062106283.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
差分电路改进：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062111183.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;积分电路设计&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/52899137&#34;&gt;积分，微分电路&lt;/a&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV17y4y1R7zX/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;电子科技队长&lt;/a&gt;&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062017211.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062020616.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
积分电路应用：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同向积分电路&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062124150.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;微分电路设计&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/52899137&#34;&gt;积分，微分电路&lt;/a&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV16A411m7Su/?spm_id_from=pageDriver&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;微分电路&lt;/a&gt;&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062154378.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306062156256.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运放和比较器的区别&lt;br&gt;
参考链接：&lt;a href=&#34;https://www.bilibili.com/read/cv9412435&#34;&gt;运算放大器和比较器区别&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;项目难点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;112-智能车选&#34;&gt;1.1.2 智能车（选）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;项目描述：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PID控制算法&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1wh411y78M/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;Acme-老朱-PID算法&lt;/a&gt;&lt;br&gt;
知识点：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306071019078.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306071021834.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;电磁车电路设计&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;环岛算法&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;项目难点（环岛算法和PID控制）&lt;br&gt;
实话实说&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;113-基于8086芯片的交通信号灯&#34;&gt;1.1.3 基于8086芯片的交通信号灯&lt;/h3&gt;
&lt;p&gt;难点：定时设置&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/34242218&#34;&gt;8086知识点总结&lt;/a&gt;&lt;br&gt;
知识点：&lt;/p&gt;
&lt;h3 id=&#34;114-linux驱动红外驱动和红外遥控器&#34;&gt;1.1.4 *linux驱动（红外驱动和红外遥控器）&lt;/h3&gt;
&lt;h3 id=&#34;115-freertos架构&#34;&gt;1.1.5 FreeRTOS架构&lt;/h3&gt;
&lt;h2 id=&#34;12-公司了解&#34;&gt;1.2 公司了解&lt;/h2&gt;
&lt;h3 id=&#34;121-芯动科技&#34;&gt;1.2.1 芯动科技&lt;/h3&gt;
&lt;p&gt;工作经历：&lt;br&gt;
2022.06-至今	浙江农商数字科技有限责任公司	java开发工程师&lt;br&gt;
1.参与完成公司数据库和系统的迁移，个人主要负责优化旧系统中存在的慢SQL，提高旧系统的运行效率，达到数据迁移的标准。&lt;br&gt;
2.基于java语言，独立开发完成了一个针对关联系统通过文件交互时，存在的取数通知失败的补偿机制。&lt;br&gt;
3.基于python语言，独立开发完成了SQL自动匹配对应回滚SQL的生产上线小工具，可以在SQL执行失败时自动回滚SQL语句。&lt;br&gt;
4.完成了3份公司日常相关业务需求开发。&lt;/p&gt;
&lt;p&gt;芯动科技：&lt;br&gt;
基于树莓派的万能遥控器（写驱动开发）---(明天学习并写简历)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;红外驱动设计&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;基于555芯片的波形转换（明天写简历）&lt;br&gt;
针对万用表，示波器&lt;br&gt;
积分，微分电路&lt;/p&gt;
&lt;p&gt;经纬恒润等车企&lt;br&gt;
基于树莓派的万能遥控器（写驱动开发）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;红外驱动设计&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;智能车（电磁+摄像头）&lt;br&gt;
环岛算法&lt;br&gt;
pid&lt;/p&gt;
">嵌入式</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/Bgv24BHxK/"" data-c="
          &lt;h1 id=&#34;1-linux驱动uboot内核移植的面试问题&#34;&gt;1. linux驱动，uboot内核移植的面试问题&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;异常优先级比中断高，不可以屏蔽&lt;br&gt;
中断上下文不能进行休眠&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;11-linux系统中自旋锁互斥锁读写锁和rcu&#34;&gt;1.1 linux系统中自旋锁，互斥锁，读写锁和RCU&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;内核当发生访问资源冲突的时候，可以有两种锁的解决方案选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个是原地等待&lt;/li&gt;
&lt;li&gt;一个是挂起当前进程，调度其他进程执行（睡眠）&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;自旋锁&lt;/strong&gt;(死等锁)&lt;br&gt;
Spinlock 是内核中提供的一种比较常见的锁机制，&lt;strong&gt;自旋锁是“原地等待”&lt;strong&gt;的方式解决资源冲突的，即，一个线程获取了一个自旋锁后，另外一个线程期望获取该自旋锁，获取不到，只能够原地“打转”（忙等待）。由于自旋锁的这个忙等待的特性，注定了它使用场景上的限制 —— 自旋锁不应该被&lt;/strong&gt;长时间的持有&lt;/strong&gt;（消耗 CPU 资源）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自旋锁的使用&lt;br&gt;
在linux kernel的实现中，经常会遇到这样的场景：共享数据被中断上下文和进程上下文访问，该如何保护呢？如果只有进程上下文的访问，那么可以考虑使用semaphore或者mutex的锁机制，但是现在&lt;strong&gt;中断上下文&lt;/strong&gt;也参和进来，那些可以导致睡眠的lock就不能使用了，这时候，可以考虑使用spin lock。&lt;br&gt;
这里为什么把中断上下文标红加粗呢？因为&lt;strong&gt;在中断上下文，是不允许睡眠的&lt;/strong&gt;（原因详见文章《&lt;a href=&#34;https://blog.csdn.net/zhoutaopower/article/details/86506725&#34;&gt;Linux 中断之中断处理浅析&lt;/a&gt;》中的第四章），所以，这里需要的是一个不会导致睡眠的锁——spinlock。&lt;br&gt;
换言之，中断上下文要用锁，首选 spinlock。&lt;/li&gt;
&lt;li&gt;自旋锁的死锁和解决&lt;br&gt;
自旋锁不可递归，自己等待自己已经获取的锁，会导致死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;内核抢占场景&lt;/strong&gt;&lt;br&gt;
（1）进程A在某个系统调用过程中访问了共享资源 R&lt;br&gt;
（2）进程B在某个系统调用过程中也访问了共享资源 R&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305301738273.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;中断上下文场景&lt;/strong&gt;&lt;br&gt;
（1）运行在CPU0上的进程A在某个系统调用过程中访问了共享资源 R&lt;br&gt;
（2）运行在CPU1上的进程B在某个系统调用过程中也访问了共享资源 R&lt;br&gt;
（3）外设P的中断handler中也会访问共享资源 R&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305301736174.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自旋锁的实现&lt;br&gt;
kernel/locking/spinlock.c&lt;br&gt;
SMP上的spin lock实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;互斥锁&lt;/strong&gt;(休眠锁)&lt;br&gt;
互斥锁实现了“互相排斥”(mutual exclusion)同步的简单形式，所以名为互斥锁。互斥锁禁止多个进程同时进入受保护的代码“临界区”（critical section）。因此，在任意时刻，只有一个进程被允许进入这样的代码保护区。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥锁的特性&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;互斥锁是Linux内核中用于互斥操做的一种同步原语；&lt;br&gt;
互斥锁是一种&lt;strong&gt;休眠锁&lt;/strong&gt;，锁争用时可能存在进程的睡眠与唤醒，context的切换带来的代价较高，适用于加锁时间较长的场景；&lt;br&gt;
互斥锁每次只容许一个进程进入临界区，有点相似于二值信号量；&lt;br&gt;
互斥锁&lt;strong&gt;在锁争用&lt;/strong&gt;时，在锁被持有时，&lt;strong&gt;选择自旋等待，而不当即进行休眠&lt;/strong&gt;，能够极大的提升性能,这种机制（optimistic spinning）也应用到了读写信号量上；&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;互斥锁的缺点&lt;br&gt;
互斥锁的缺点是互斥锁对象的结构较大，会占用更多的CPU缓存和内存空间；&lt;/li&gt;
&lt;li&gt;互斥锁的优点&lt;br&gt;
与信号量相比，互斥锁的性能与扩展性都更好，所以，在内核中老是会优先考虑互斥锁；&lt;/li&gt;
&lt;li&gt;互斥锁性能提升&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;互斥锁按为了提升性能，提供了三条路径处理：&lt;strong&gt;快速路径，中速路径，慢速路径&lt;/strong&gt;；&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;互斥锁的代码&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;定义互斥锁：struct mutex my_mutex;&lt;br&gt;
初始化互斥锁：mutex_init(&amp;amp;my_mutex);&lt;br&gt;
使用宏定义并初始化互斥锁：DEFINE_MUTEX(my_mutex)&lt;br&gt;
获取互斥锁：void mutex_lock(struct mutex *lock); //会进入睡眠，中断上下文避免使用&lt;br&gt;
获取互斥锁：int mutex_lock_interruptible(struct mutex *lock);//进入睡眠后，会被中断&lt;br&gt;
释放互斥锁：void mutex_unlock(struct mutex *lock);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;读写锁&lt;/strong&gt;&lt;br&gt;
读写锁其实还是一种锁，是给一段临界区代码加锁，但是此加锁是在进行写操作的时候才会互斥，而在进行读的时候是可以共享的进行访问临界区的&lt;br&gt;
ps：读写锁本质上是一种自旋锁&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读写锁的加锁规则&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305302348992.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;读写锁怎么实现？&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;1.一种交易场所（存放数据的地方）：可以是变量、链表、数组或其他数据结构
2.两种角色：读操作和写操作
3.三种关系：（1）读和读之间没有关系
            （2） 写和写之间是互斥关系
            （3）读和写之间是同步互斥关系
ps：同步----&amp;gt;读和写在同时竞争锁的时候，写会优先的得到锁
        互斥----&amp;gt;读的时候写阻塞，写的时候读阻塞
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;相关函数
&lt;ul&gt;
&lt;li&gt;pthread_rwlock_init()—-&amp;gt;初始化函数&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;功能：初始化读写锁
头文件：#include&amp;lt;pthread.h&amp;gt;
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthred_rwlockattr_t *restrict attr);
参数说明：
rwlock：是要进行初始化的
attr：是rwlock的属性
ps：此参数一般不关注，可设为NULL
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;pthread_rwlock_destroy—-&amp;gt;销毁函数&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;功能：销毁初始化的锁
头文件：#include&amp;lt;pthread.h&amp;gt;
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数说明：
rwlock:是需要进行销毁的锁
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;加锁和解锁&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;在进行读操作的时候加的锁：
pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
在进行写操作的时候加的锁：
pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
对读/写统一进行解锁：
pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RCU&lt;/strong&gt;（读写锁升级版）&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/89439043&#34;&gt;知乎：术道经纬&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基本原理：类似java的ArrayList的拷贝&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;12-bootloader加载期间不区分数据段和代码段&#34;&gt;1.2 bootloader加载期间不区分数据段和代码段&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/iduuigdg/article/details/122144655&#34;&gt;「已注销」的博客&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;13-c容器&#34;&gt;1.3 c++容器&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/NXHYD/article/details/108072498&#34;&gt;AliceWanderAI的博客C++的STL组件&lt;/a&gt;&lt;br&gt;
容器是STL中很重要的一种数据结构。常见的容器包括&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;vector容器&lt;br&gt;
vector是一种动态数组，在内存中具有&lt;strong&gt;连续的存储空间&lt;/strong&gt;，支持&lt;strong&gt;快速随机&lt;/strong&gt;访问。由于具有连续的存储空间，所以在插入和删除操作方面，效率比较慢。vector有多个构造函数，默认的构造函数是构造一个&lt;strong&gt;初始长度为0&lt;/strong&gt;的内存空间，且分配的内存空间是以2的倍数动态增长的。在push_back的过程中，若发现分配的内存空间不足，则重新分配一段连续的内存空间，其大小是现在&lt;strong&gt;连续空间的2倍&lt;/strong&gt;，再将原先空间中的元素&lt;strong&gt;复制&lt;/strong&gt;到新的空间中，性能消耗比较大。&lt;br&gt;
vector的另一个常见的问题就是clear操作。clear函数只是把vector的size清为零，但vector中的元素在内存中并没有消除，所以在使用vector的过程中会发现内存消耗会越来越多，导致内存泄露，现在经常用的方法是swap函数来进行解决：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;vector&amp;lt;int&amp;gt; V;
V.push_back(1); 
V.push_back(2);
V.push_back(1); 
V.push_back(2);
vector&amp;lt;int&amp;gt;().swap(V); 
//或者 V.swap(vector&amp;lt;int&amp;gt;());
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;deque双端数组&lt;br&gt;
deque和vector类似，支持&lt;strong&gt;快速随机访问&lt;/strong&gt;。二者最大的区别在于，vector只能在末端插入数据，而&lt;strong&gt;deque支持双端&lt;/strong&gt;插入数据。&lt;br&gt;
deque的内存空间分布是小片的连续，小片间用链表相连，实际上内部有一个map的指针。deque空间的&lt;strong&gt;重新分配要比vector快&lt;/strong&gt;，重新分配空间后，原有的元素是不需要拷贝的。&lt;br&gt;
与vector不同的是，deque还支持从&lt;strong&gt;开始端插入数据&lt;/strong&gt;：&lt;code&gt;push_front()&lt;/code&gt;。其余类似vector操作方法的使用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;push_back&lt;/code&gt; 从尾部插入元素&lt;/li&gt;
&lt;li&gt;&lt;code&gt;push_front&lt;/code&gt; 从头部插入元素&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pop_back&lt;/code&gt; 从尾部删除元素&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pop_front&lt;/code&gt; 从头部删除元素&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;distance函数可以求出当前的迭代器指针it距离头部的位置，也就是容器的指针&lt;br&gt;
&lt;strong&gt;用法&lt;/strong&gt;: distance(v1.begin(), it)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;list链表模型&lt;br&gt;
list是一个双向链表，因此它的&lt;strong&gt;内存空间是可以不连续&lt;/strong&gt;的，通过指针来进行数据的访问，这使list的&lt;strong&gt;随机存储变得非常低效&lt;/strong&gt;，因此list没有提供[]操作符的重载。但list可以很好地支持&lt;strong&gt;任意地方的插入和删除&lt;/strong&gt;，只需移动相应的指针即可。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;list常见操作&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;lst1.assign(lst2.begin(),lst2.end());  //分配值
lst1.push_back(10);                    //添加值
lst1.pop_back();                   //删除末尾值
lst1.begin();                      //返回首值的迭代器
lst1.end();                            //返回尾值的迭代器
lst1.clear();                      //清空值
bool isEmpty1 = lst1.empty();          //判断为空
lst1.erase(lst1.begin(),lst1.end());                        //删除元素
lst1.front();                      //返回第一个元素的引用
lst1.back();                       //返回最后一个元素的引用
lst1.insert(lst1.begin(),3,2);         //从指定位置插入3个值为2的元素
lst1.rbegin();                         //返回第一个元素的前向指针
lst1.remove(2);                        //相同的元素全部删除
lst1.reverse();                        //反转
lst1.size();                       //含有元素个数
lst1.sort();                       //排序
lst1.unique();                         //删除相邻重复元素
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;set与multiset容器&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;set和multiset的区别&lt;/strong&gt;：set插入的元素不能相同，但是multiset可以相同。Set默认自动排序。使用方法类似list。&lt;br&gt;
set也是一种关联性容器，它同map一样，底层使用&lt;strong&gt;红黑树&lt;/strong&gt;实现，插入删除操作时仅仅移动指针即可，不涉及内存的移动和拷贝，所以&lt;strong&gt;效率比较高&lt;/strong&gt;。&lt;br&gt;
set的含义是集合，它是一个有序的容器，里面的元素都是排序好的，支持&lt;strong&gt;插入，删除，查找&lt;/strong&gt;等操作，就像一个集合一样。所有的操作的都是严格在&lt;strong&gt;logn&lt;/strong&gt;时间之内完成，效率非常高。&lt;br&gt;
set中的元素都是&lt;strong&gt;唯一&lt;/strong&gt;的，而且默认情况下会对元素进行&lt;strong&gt;升序排列&lt;/strong&gt;。&lt;br&gt;
所以在set中，不能直接改变元素值，因为那样会打乱原本正确的顺序，要&lt;strong&gt;改变元素值必须先删除&lt;/strong&gt;旧元素，再插入新元素。不提供直接存取元素的任何操作函数，&lt;strong&gt;只能通过迭代器进行间接存取&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;+ 创建集合的方式：
    + `set&amp;lt;int&amp;gt;`创建默认的从小到大的int类型的集合
    + `set&amp;lt;int,less&amp;lt;int&amp;gt;&amp;gt;`创建一个从小打到大的int类型的集合
    + `set&amp;lt;int,greater&amp;lt;int&amp;gt;&amp;gt;`创建一个从大到小的int类型的集合
 上面的less和greater就是仿函数，集合会根据这个仿函数的返回值是否为真类进行排序。
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;map与multimap容器&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;map和multimap唯一区别&lt;/strong&gt;：multimap支持多个键值。由于支持多个键值，multimap提供了cout函数来计算同一个key的元素个数。&lt;br&gt;
map 是键－值对的集合。map 类型通常可理解为关联数组：可使用键作为下标来获取一个值，正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联，而并非通过元素在数组中的位置来获取。&lt;br&gt;
map内部自建一棵&lt;strong&gt;红黑树（一种自平衡二叉树）&lt;/strong&gt;，这棵树具有数据&lt;strong&gt;自动排序&lt;/strong&gt;的功能，所以在&lt;strong&gt;map内部所有的数据都是有序的&lt;/strong&gt;，以二叉树的形式进行组织。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;queue队列模型&lt;br&gt;
queue是一个队列，实现&lt;strong&gt;先进先出&lt;/strong&gt;功能，&lt;strong&gt;queue不是标准的STL容器，却以标准的STL容器为基础&lt;/strong&gt;。&lt;strong&gt;queue是在deque的基础上封装的&lt;/strong&gt;。之所以选择deque而不选择vector是因为&lt;strong&gt;deque在删除元素的时候释放空间&lt;/strong&gt;，同时在重新申请空间的时候&lt;strong&gt;无需拷贝所有元素&lt;/strong&gt;。&lt;br&gt;
和 stack 一样，queue 也&lt;strong&gt;没有迭代器&lt;/strong&gt;。访问元素的唯一方式是遍历容器内容，并移除访问过的每一个元素。&lt;/li&gt;
&lt;/ul&gt;
&lt;center&gt;Queue操作&lt;/center&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;q.pop()&lt;/td&gt;
&lt;td&gt;移除q的首元素或priority_queue的最高优先级的元素，返回void&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q.front()&lt;/td&gt;
&lt;td&gt;访问首元素或尾元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q.back()&lt;/td&gt;
&lt;td&gt;访问尾元素，只适用于queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q.top()&lt;/td&gt;
&lt;td&gt;访问最高优先级元素，只适用于priority_queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q.push(item)&lt;/td&gt;
&lt;td&gt;在queue末尾或priority_queue中恰当位置创造一个元素，其值为item, 或者又args构造&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q.emplace(args)&lt;/td&gt;
&lt;td&gt;同上&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;stack栈模型&lt;br&gt;
stack是实现先进后出的功能，和queue一样，也是内部封装了deque。stack的源代码原理和实现方式均跟queue相同。&lt;/li&gt;
&lt;/ul&gt;
&lt;center&gt;Queue操作&lt;/center&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;stk.top()&lt;/td&gt;
&lt;td&gt;访问栈顶元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stk.push(value)&lt;/td&gt;
&lt;td&gt;将value压入栈内&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stk.emplace(seq)&lt;/td&gt;
&lt;td&gt;用seq构造元素并压栈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stk.pop()&lt;/td&gt;
&lt;td&gt;将当前栈顶元素出栈，返回类型void&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;priotriy_queue优先级队列&lt;br&gt;
优先级队列（priority_queue）其实，不满足先进先出的条件，更像是数据类型中的“堆”。优先级队列每次出队的元素是队列中优先级最高的那个元素，而不是队首的元素。这个优先级可以通过元素的大小等进行定义。比如定义元素越大优先级越高，那么每次出队，都是将当前队列中最大的那个元素出队。个人感觉这就是所谓“优先级”的定义。
&lt;ul&gt;
&lt;li&gt;优先级队列的定义&lt;br&gt;
C++中，使用优先级队列需要包含头文件&lt;queue&gt;，优先级队列的定义如下：&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;priority_queue&amp;lt;typename, container, functional&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;typename是数据的类型；&lt;br&gt;
container是容器类型，可以是vector,queue等用数组实现的容器，不能是list，默认可以用vector；&lt;br&gt;
functional是比较的方式，默认是大顶堆（就是元素值越大，优先级越高）；如果使用C++基本数据类型，可以直接使用自带的less和greater这两个仿函数（默认使用的是less，就是构造大顶堆，元素小于当前节点时下沉）。使用自定义的数据类型的时候，可以重写比较函数，也可以进行运算符重载（less重载小于“&amp;lt;”运算符，构造大顶堆；greater重载大于“&amp;gt;”运算符，构造小顶堆）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定义一个优先级队列的示例如下&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;//构造一个大顶堆，堆中小于当前节点的元素需要下沉，因此使用less
priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, less&amp;lt;int&amp;gt;&amp;gt; priQueMaxFirst;

//构造一个小顶堆，堆中大于当前节点的元素需要下沉，因此使用greater
priority_queue&amp;lt;string, vector&amp;lt;string&amp;gt;, greater&amp;lt;string&amp;gt;&amp;gt; priQueMinFirst;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过重写仿函数来支持自定义数据类型&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;//自定义数据类型，Data类
class Data
{
public:
    Data(int i, int d) :id(i), data(d) {}
    ~Data() {}
    int getId() { return id; }
    int getData() { return data; }
private:
    int id;
    int data;
};
//重写仿函数，完成less的功能，也可以用class定义类，此时需要将运算符重载函数设为public
//结构体struct中默认是访问类型是public
struct cmp    
{
    bool operator() ( Data &amp;amp;a, Data &amp;amp;b) {
        return a.getId() &amp;lt; b.getId();
    }
};

int main(void){
    priority_queue&amp;lt;Data, vector&amp;lt;Data&amp;gt;, cmp &amp;gt; priQueMaxFirst;//该优先级队列维护一个大顶堆，因此最大的元素最先出队
    ...//一系列操作
    ...
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;14-arm体系架构&#34;&gt;1.4 ARM体系架构&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SOC（System on Chip）片上系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARM内核架构图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305312152492.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
ARM内核进行运算的核心部件是算术逻辑运算单元-ALU（arithmetic and logic unit）。它对两个操作数进行逻辑或者算术运算。为了提高嵌入式处理器的工作速度，以保证实时性的要求，ARM在处理器中尽可能多地设置了寄存器。ARM处理器共有37个32位寄存器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;冯诺依曼结构和哈佛结构&lt;br&gt;
冯•诺依曼结构也称普林斯顿结构，是一种将程序&lt;strong&gt;指令存储器和数据存储器合并在一起的存储器结构&lt;/strong&gt;。程序指令存储地址和数据存储地址指向同一个存储器的不同物理地址，因此&lt;strong&gt;程序指令和数据的宽度相同&lt;/strong&gt;。&lt;br&gt;
哈佛结构是一种将程序&lt;strong&gt;指令存储和数据指令存储分开的存储器结构&lt;/strong&gt;。中央处理器首先到程序存储器中读取程序指令内容，解码后得到数据地址，再到相应的数据存储器中读取数据，并进行下一步操作。程序指令存储和数据存储分开，&lt;strong&gt;可以使指令和数据有不同的数据宽度&lt;/strong&gt;。&lt;br&gt;
哈佛结构的微处理器通常具有较高的执行效率。ARM9、ARM10和ARM11属于哈佛结构&lt;br&gt;
&lt;img src=&#34;https://pic1.zhimg.com/80/v2-7039a18007fdd5440a637fcb36fb7ec8_1440w.webp&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARM处理器的运行模式&lt;br&gt;
运行不同的程序所需的硬件资源不同，因此ARM处理器可以为程序提供7种不同的硬件资源组合，每一种硬件资源组合叫做一种运行模式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;USR(用户模式)&lt;/strong&gt;：ARM处理器正常程序执行模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FIQ(快速中断模式)&lt;/strong&gt;：用于高速数据传输或通道处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IRQ(中断模式)&lt;/strong&gt;：用于通用的中断处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SVC(管理模式)&lt;/strong&gt;：操作系统使用的保护模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ABT(终止模式)&lt;/strong&gt;：当数据或指令预取出错时进入的模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SYS(系统模式)&lt;/strong&gt;：运行具有特权的操作系统任务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UND(未定义指令中止模式)&lt;/strong&gt;：当处理器试图执行未定义指令时进入的模式&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;相对于用户模式来说，对硬件资源的使用有某种特权的运行模式叫做特权模式（除了用户模式其他六种运行模式都是特权模式）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ARM的两种工作状态&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ARM指令集和Thumb指令集
&lt;ul&gt;
&lt;li&gt;完成相同的操作，Thumb指令通常需要更多的指令，因此在对系统运行时间要求苛刻的应用场合ARM指令集更为适合;&lt;/li&gt;
&lt;li&gt;Thumb指令集没有包含进行异常处理时需要的一些指令，因此在异常中断时，还是需要使用ARM指令，这种限制决定了Thumb指令需要和ARM指令配合使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARM处理器的寄存器&lt;br&gt;
&lt;strong&gt;ARM状态下寄存器的组织方式&lt;/strong&gt;：虽然ARM处理器共有37个寄存器，但是根据处理器的运行模式，程序能够真正使用的只是其中的18个或者17个。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基础基础器&lt;br&gt;
R0&lt;sub&gt;R15和CPSR，其中R0&lt;/sub&gt;R7、R15和CPSR为所有运行模式共享；R8~R12为除了快中断模式之外的其他所有运行模式共享；R13和R14为用户模式和系统模式共享 ，如下图所示：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305312217045.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;模式私有寄存器&lt;br&gt;
ARM为某些运行模式设置了一些只能在该运行模式下使用的私有寄存器；ARM规定，当处理器在某种模式运行时，凡是与该模式私有寄存器序号相同的基本寄存器都将被禁用。例如在终止模式下，与该模式私有寄存器R13_abt~R14_abt序号相同的基础寄存器R13和R14就处于禁用状态。&lt;/li&gt;
&lt;li&gt;程序计数器R15（pc）&lt;br&gt;
在ARM中，基础寄存器R15固定得作为程序计数器来使用。为了提高程序的可读性，通常用PC来标识。&lt;/li&gt;
&lt;li&gt;程序状态寄存器PSR&lt;br&gt;
基础寄存器R16专门用作程序状态寄存器。为了提高程序的可读性，程序中也可以称为PSR&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305312222759.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
ARM处理器中有6个PSR，当前程序状态寄存器CPSR是所有运行模式所共享的，而另外5个PSR为模式私有，用来对CPSR进行备份的寄存器叫做SPSR。&lt;br&gt;
由于CPSR中保存的是程序当前运行模式的状态信息，所以当前运行模式发生变化时，为了防止这些信息丢失，ARM对每种异常运行模式都设置了一个&lt;strong&gt;与CPSR的格式完全相同的备份寄存器SPSR&lt;/strong&gt;（Saved Progarm Status Register）。即当处理器进入&lt;strong&gt;异常运行模式&lt;/strong&gt;时，系统会自动把CPSR的当前值转存到SPSR；当从异常模式退出时，再自动把当初保存到SPSR中的状态信息存回CPSR。&lt;br&gt;
由于用户模式和系统模式不属于异常模式，所以这两个模式没有SPSR。&lt;/li&gt;
&lt;li&gt;堆栈指针寄存器R13（SP）&lt;br&gt;
堆栈是计算机存储数据的一种&lt;strong&gt;数据结构&lt;/strong&gt;，SP的作用就是指示当前要出栈或入栈的数据，并在&lt;strong&gt;操作执行后自动递增或递减&lt;/strong&gt;。&lt;br&gt;
计算机中的堆栈主要用来保存临时数据，局部变量和&lt;strong&gt;中断/调用子程序程序的返回地址&lt;/strong&gt;。程序中栈主要是用来存储函数中的&lt;strong&gt;局部变量&lt;/strong&gt;以及&lt;strong&gt;保存寄存器参数&lt;/strong&gt;。
&lt;ol&gt;
&lt;li&gt;保存现场；&lt;/li&gt;
&lt;li&gt;传递参数:汇编代码调用 C 函数时，需传递参数；&lt;/li&gt;
&lt;li&gt;保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量；&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;链接寄存器R14（LR）&lt;br&gt;
寄存器R14也称为子程序链接寄存器（Subroutine Link Register）或链接寄存器LR。当执行&lt;strong&gt;子程序调用指令BL时&lt;/strong&gt;，&lt;strong&gt;R14会备份R15（程序计数器PC）的内容&lt;/strong&gt;，以便子程序结束后能使程序正确地返回。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;存储器的组织&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据存储方式&lt;br&gt;
ARM是一种32位处理器，即ARM处理器处理的数据通常是以32位二进制位基本单位的。ARM中的一个32位数叫做一个字。在存储器中，ARM的一个基本数据需要占用4个连续存储单元，为了判断哪四个单元为一个字，ARM规定一个字所占用的4个连续存储单元的&lt;strong&gt;第一个单元地址的低2位必须是00&lt;/strong&gt;。&lt;br&gt;
根据字的4字节在4个存储单元中的存储顺序，ARM提供了两种存储方式：小端方式和大端方式。系统默认的是小端方式，即字的低位字节在地址的底端（小端）。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305312233298.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;ARM的堆栈组织方式&lt;br&gt;
在计算机应用中，常常需要在内存中建立一种特殊工作方式的存储区域，这种存储区域中数据的读/写方式是按先进后出（First In Last Out，FILO）的原则来组织的，这种存储区域就叫做堆栈。&lt;br&gt;
为了只是堆栈的位置，计算机系统都有一个堆栈指针的专用寄存器来存放堆栈的地址：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305312236208.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
堆栈按照其生长方式可分为两种：&lt;strong&gt;地址递增式、地址递减式&lt;/strong&gt;&lt;br&gt;
在系统初始化时，如果把&lt;strong&gt;堆栈指针置于较低&lt;/strong&gt;的地址，当向堆栈存入一个数据时，堆栈的指针&lt;strong&gt;会指向高地址移动&lt;/strong&gt;，即下次再存入数据时，数据的存放地址位于先存入数据上方的这个堆栈叫做地址递增式的堆栈。反之，如果数据的存储方向刚好与地址递增方式的堆栈的方向相反，则这种堆栈就叫做地址递减式堆栈。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARM体系结构的其他特点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ARM协处理器接口&lt;br&gt;
在ARM中，可以通过ARM核的协处理器接口配置一个或多个协处理器的方法来增强ARM处理器的功能。一个ARM核最多可以配置16个协处理器。&lt;br&gt;
ARM体系结构有三组总线，可以方便地扩充各种处理器、DSP或I/O。
&lt;ul&gt;
&lt;li&gt;AHB: Advanced High performance Bus 高级高性能总线&lt;/li&gt;
&lt;li&gt;ASB: Advanced System Bus 高级系统总线----用的很少&lt;/li&gt;
&lt;li&gt;APB: Advanced Peripherial Bus 高级外围总线&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ARM在线仿真调试&lt;br&gt;
为了对处理器进行仿真调试，大多数基于ARM的处理器嵌入了ICE-RT逻辑，以便通过JTAG来仿真调试。另外，处理器还可以嵌入跟踪宏单元ETM（Embedded Trace Macrocell），用于监控内部总线，以实时跟踪指令和数据的执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;15-uartspiusbi2ccan总线&#34;&gt;1.5 uart，spi，usb，i2c，can总线&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;串行通信：距离长，传输速率慢&lt;br&gt;
并行通信：距离端，传输速率快&lt;br&gt;
同步通信：有统一时钟信号&lt;br&gt;
异步通信：无时钟信号，以数据帧的的格式发送&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;串行通信：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040105627.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UART&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;串行异步全双工通信&lt;/strong&gt;，通信双方有相同&lt;strong&gt;波特率&lt;/strong&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1cQ4y1C7hj/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;硬声APP-UART&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;数据帧格式详解&lt;br&gt;
参考博客链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/182321407&#34;&gt;记得诚-uart&lt;/a&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041030989.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;起始位：发送1位逻辑0（低电平），开始传输数据。&lt;br&gt;
数据位：可以是5~8位的数据，先发低位，再发高位，一般常见的就是8位（1个字节），其他的如7位的ASCII码。&lt;br&gt;
校验位：奇偶校验，将数据位加上校验位，1的位数为偶数（偶校验），1的位数4为奇数（奇校验）。&lt;br&gt;
停止位：停止位是数据传输结束的标志，可以是1/1.5/2位的逻辑1（高电平）。&lt;br&gt;
空闲位：空闲时数据线为高电平状态，代表无数据传输。&lt;br&gt;
例如传输0x33(00110011)，那么对应的波形就是如下这样，因为是LSB在前，所以8位数据依次是11001100&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041037405.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;四种工作方式&lt;br&gt;
参考链接：&lt;a href=&#34;https://blog.csdn.net/qq_40459977/article/details/106926139&#34;&gt;咖啡乌龙-uart四种工作方式&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SPI&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;串行同步半双工数据通信&lt;/strong&gt;，&lt;strong&gt;一主多从&lt;/strong&gt;，&lt;strong&gt;一般上升沿数据有效&lt;/strong&gt;&lt;br&gt;
参数视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1F54y1M7e7/?spm_id_from=333.999.0.0&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-spi总线&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;四条信号线&lt;br&gt;
1. SS(片选信号线)&lt;br&gt;
用来确定选择哪个从芯片通信&lt;br&gt;
低电平有效&lt;br&gt;
2. SCK(时钟信号线)&lt;br&gt;
主设备产生&lt;br&gt;
3. MOSI(主设备输出数据线)&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040037186.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
4. MISO(主设备接受数据线)&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040038426.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;数据采集方式：&lt;br&gt;
上升下降沿等都可以，由具体设备决定&lt;br&gt;
时序图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040043561.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;USB&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I2C&lt;/strong&gt;:&lt;br&gt;
&lt;strong&gt;一主多从模式&lt;/strong&gt;，&lt;strong&gt;串行同步全双工&lt;/strong&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1dg4y1H773/?spm_id_from=333.999.0.0&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-I2C总线&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;两条信号线&lt;br&gt;
1. SCL（时钟信号线）&lt;br&gt;
2. SDA（数据信号线）&lt;/li&gt;
&lt;li&gt;数据传输比特0和1&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040052776.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;
&lt;ul&gt;
&lt;li&gt;传输数据起始和结束&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040056043.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;读写数据的帧格式&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040059488.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;总结&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040058790.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CAN&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;采用&lt;strong&gt;差分传递数据&lt;/strong&gt;，多个设备挂载&lt;strong&gt;同一总线&lt;/strong&gt;的方式链接&lt;br&gt;
视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV14k4y187e6/?spm_id_from=333.1007.tianma.2-2-5.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;爱上半导体-can&lt;/a&gt;&lt;br&gt;
帧格式&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306040028284.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;16-awksedgrep&#34;&gt;1.6 awk，sed，grep&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/110983126&#34;&gt;Beritra的知乎文章&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;17-linux中断上下文&#34;&gt;1.7 *linux中断上下文&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/113002536&#34;&gt;韦东山的中断上下文&lt;/a&gt;&lt;br&gt;
参考链接：&lt;a href=&#34;https://blog.csdn.net/qq_38500662/article/details/80598486&#34;&gt;kyo_博客的什么是中断上下文详解&lt;/a&gt;&lt;/p&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;1&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306021406503.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h2 id=&#34;18-c语言内存分区&#34;&gt;1.8 c语言内存分区&lt;/h2&gt;
&lt;p&gt;参考博客：&lt;a href=&#34;/9_Fn4Xz4p&#34;&gt;C语言内存分区&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;19-pinctrl和gpioctrlplatform&#34;&gt;1.9 *pinctrl和gpioctrl，platform&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;platform总线&lt;/strong&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041544822.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;platform_driver和platform_device的使用&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041548508.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;platform_driver和platform_device匹配&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041552999.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;match：函数用来driver和device的比较匹配&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;platform_driver和device的匹配规则&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041605826.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;
&lt;ol&gt;
&lt;li&gt;首先匹配&lt;code&gt;platform_device&lt;/code&gt;中&lt;code&gt;driver_override&lt;/code&gt;和 &lt;code&gt;platform_driver&lt;/code&gt;的&lt;code&gt;name&lt;/code&gt;匹配（优先级高）&lt;/li&gt;
&lt;li&gt;若上一步匹配失败，则匹配&lt;code&gt;platform_device&lt;/code&gt;中&lt;code&gt;name&lt;/code&gt;和 &lt;code&gt;platform_driver&lt;/code&gt;的&lt;code&gt;id_table&lt;/code&gt;里的&lt;code&gt;name&lt;/code&gt;一一匹配（优先级中）&lt;/li&gt;
&lt;li&gt;若前两步匹配失败，则匹配&lt;code&gt;platform_device&lt;/code&gt;中&lt;code&gt;name&lt;/code&gt;和 &lt;code&gt;platform_driver&lt;/code&gt;的&lt;code&gt;name&lt;/code&gt;匹配（优先级低）&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;函数调用关系&lt;br&gt;
device_register关系&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041610549.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
driver_register关系&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041611134.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;应用层，驱动层，设备层的职责&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041813902.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;设备树&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;可以使用C语言的语法&lt;code&gt;#include&amp;lt;xxxx.h&amp;gt;&lt;/code&gt;，包含其他设备树文件&lt;br&gt;
&lt;strong&gt;bootloader负责将dtb文件传递给内核&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;cd /sys/firmware/devicetree&lt;/code&gt;：系统设备树文件存放路径&lt;br&gt;
参考博客链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/165570521&#34;&gt;听心跳的声音-设备树&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;基础语法&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;&amp;quot;：字符串&lt;br&gt;
&amp;lt;&amp;gt;：32位数字&lt;br&gt;
[ ] ：16进制数字&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;cpu属性&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;memory属性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041825301.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;compatible属性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041826162.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;model属性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041829295.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;status属性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041832353.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;reg属性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041834650.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name属性（过时了）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041837514.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;device_type属性（过时了）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041838669.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设备树编译&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306041848956.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内核对设备树的处理&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042144224.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;会被转为platform_device节点的设备树节点&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;设备树的根设备节点含有&lt;code&gt;compatile&lt;/code&gt;属性则会被转换为&lt;code&gt;platform_device&lt;/code&gt;节点&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042145832.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;设备树匹配设备驱动程序&lt;/strong&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042239235.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042240886.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042237896.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;获取设备资源和中断信息&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042242879.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不会生成platform_device设备的节点查找&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;先根据函数，找到对应设备树的节点&lt;br&gt;
根据节点，查找对应属性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042244927.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;pinctrl子系统&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042251431.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;pincontroller和client的设备树文件配置关系&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306042300242.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;gpio子系统&lt;/strong&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;&#34;&gt;韦东山-GPIO子系统概述&lt;/a&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1w4411B7a4?p=116&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;韦东山-GPIO子系统的LED灯驱动&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;110-linux中软链接和硬链接&#34;&gt;1.10 linux中软链接和硬链接&lt;/h2&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;2&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306012309992.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h2 id=&#34;111-sysfs节点&#34;&gt;1.11 sysfs节点&lt;/h2&gt;
&lt;p&gt;sys目录结构&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306012338936.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;p&gt;sysfs可以看成与&lt;strong&gt;proc,devfs和devpty&lt;/strong&gt;同类别的&lt;strong&gt;文件系统&lt;/strong&gt;，该文件系统是&lt;strong&gt;虚拟的文件系统&lt;/strong&gt;，可以更方便对系统设备进行管理。它可以产生一个包含所有系统硬件层次视图，与提供进程和状态信息的proc文件系统十分类似。&lt;/p&gt;
&lt;p&gt;sysfs把连接在系统上的设备和总线组织成为一个分级的文件，它们可以&lt;strong&gt;由用户空间存取&lt;/strong&gt;，向用户空间导出内核的数据结构以及它们的属性。sysfs的一个目的就是&lt;strong&gt;展示设备驱动模型中各组件的层次关系&lt;/strong&gt;，其顶级目录包括block,bus,drivers,class,power和firmware等.&lt;/p&gt;
&lt;p&gt;sysfs提供一种机制，使得可以显式的描述&lt;strong&gt;内核对象、对象属性及对象间关系&lt;/strong&gt;。sysfs有两组接口，一组针对内核，用于将设备映射到文件系统中，另一组针对用户程序，用于读取或操作这些设备。表2描述了内核中的sysfs要素及其在用户空间的表现：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;sysfs在内核中的组成要素&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;在用户空间的显示&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;内核对象（kobject）&lt;/td&gt;
&lt;td&gt;目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;对象属性（attribute）&lt;/td&gt;
&lt;td&gt;文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;对象关系（relationship）&lt;/td&gt;
&lt;td&gt;链接（Symbolic Link）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;112-haldmaidlepolling&#34;&gt;1.12 HAL,DMA,IDLE,POLLING&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;HAL&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/387447152&#34;&gt;编程小霸王的博客&lt;/a&gt;&lt;br&gt;
参考链接：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/250602562&#34;&gt;Nixxx的博客&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;DMA&lt;br&gt;
参考链接：&lt;a href=&#34;https://www.jianshu.com/p/12f00061b223&#34;&gt;听力巴士的博客&lt;/a&gt;&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1jW4y1k7cg/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;阿熊话太多的视频&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IDLE&lt;/li&gt;
&lt;li&gt;POLLING&lt;br&gt;
参考博客链接：&lt;a href=&#34;https://blog.csdn.net/qq_28499879/article/details/123870478&#34;&gt;菜菜的阿庄&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;polling即轮询模式，是一种软件协议。&lt;br&gt;
在轮询过程中，CPU周期性的依次询问每一个设备，是否有业务需要处理。&lt;br&gt;
每个设备有一个指令就绪bit位，这个bit位标识了设备是否需要处理器服务的状态。当这个状态位被置位，CPU将处理该设备上的指令。&lt;/p&gt;
&lt;h2 id=&#34;113-linux多线程一个线程访问某个外设驱动怎么防止其他线程访问&#34;&gt;1.13 linux多线程：一个线程访问某个外设驱动，怎么防止其他线程访问&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://www.cnblogs.com/chen-farsight/p/6139416.html&#34;&gt;Green的博客&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;通俗来说就是并发控制，即竞态问题解决&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;并发&lt;/strong&gt;：指在同一时刻只能有一条指令执行，但多个进程指令被快速的轮换执行，使得在宏观上具有多个进程同时执行的效果，但在微观上并不是同时执行的，只是把时间分成若干段，使多个进程快速交替的执行。&lt;br&gt;
&lt;strong&gt;并行&lt;/strong&gt;：指在同一时刻，有多条指令在多个处理器上同时执行。&lt;br&gt;
&lt;strong&gt;竞态（race condition）&lt;/strong&gt;：简单来讲，竞态就是指多个执行序列同时访问同一个共享资源的状况；&lt;br&gt;
&lt;strong&gt;临界区（critical sections）&lt;/strong&gt;：访问共性资源的代码区域称为临界区，临界区需要被以某种互斥机制加以保护；&lt;br&gt;
&lt;strong&gt;临界资源&lt;/strong&gt;：是指一段时间内只允许一个进程访问的资源&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在Linux内核中，主要的竞态发生于如下几种情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对称多处理器（SMP）的多个CPU&lt;br&gt;
SMP是一种紧密耦合、共享存储的系统模型，它的特点是&lt;strong&gt;多个CPU使用共同的系统总线&lt;/strong&gt;，因此可访问共同的外设和存储器。&lt;/li&gt;
&lt;li&gt;单CPU内进程与抢占它的进程&lt;br&gt;
一个进程在内核执行的时候可能被另一高优先级进程打断。&lt;/li&gt;
&lt;li&gt;中断（硬中断，软中断，Tasklet，下半部）与进程之间的&lt;br&gt;
&lt;strong&gt;中断可以打断正在执行的进程&lt;/strong&gt;，如果中断处理程序访问进程正在访问的资源，则竞态就会发生。此外，中断也有可能被更高优先级的中断打断，因此，&lt;strong&gt;多个中断之间本身也可能引起并发而导致竞态&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;竞态问题的解决方法&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;中断屏蔽&lt;br&gt;
在&lt;strong&gt;单CPU范围内&lt;/strong&gt;避免竞态的一种简单省事的方法是在进入临界区之前&lt;strong&gt;屏蔽系统的中断&lt;/strong&gt;，这项功能可以保证正在执行的内核执行路径不被中断处理程序所抢占，&lt;strong&gt;防止某些竞态条件的发生&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;1）中断屏蔽将使得中断与进程之间的并发不再发生&lt;br&gt;
2）由于Linux内核的进程调度等操作都依赖中断来实现，&lt;strong&gt;内核抢占&lt;/strong&gt;与&lt;strong&gt;进程之间&lt;/strong&gt;的并发就得意避免了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;中断屏蔽的使用方法&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;    local_irq_disable()　　/*屏蔽中断*/
    ...
    critical section　　/*临界区*/
    ...
    local_irq_enable()　　/*打开中断*/
    local_irq_disable()  
    local_irq_enable()  
    /* 只能禁止和使能本地CPU的中断，所以不能解决多CPU引发的竞态  */
      
    local_irq_save(flags)  
    local_irq_restore(flags)  
    /* 除了能禁止和使能中断外，还保存和还原目前的CPU中断位信息  */
      
    local_bh_disable()  
    local_bh_disable()  
    /* 如果只是想禁止中断的底半部，这是个不错的选择 */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;br&gt;
1）由于Linux的异步I/O、进程调度等很多重要操作依赖于中断，中断对于内核的执行非常重要，在屏蔽中断期间说有的中断都无法得到处理，因此产时间屏蔽中断是很危险的，有可能造成数据丢失乃至系统崩溃等后果，因此在屏蔽了中断之后，当前的内核执行路径应当尽快的执行完临界区的代码；&lt;br&gt;
2）单独使用中断屏蔽&lt;strong&gt;不是一种值得推荐的避免竞态的方式&lt;/strong&gt;，它宜与自旋锁联合使用。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;原子操作&lt;br&gt;
原子操作（整型原子操作和位原子操作）是在执行过程不会被别的代码路径所中断的操作，它在任何情况下操作都是原子的，内核代码可以安全的调用它们而不被打断。&lt;br&gt;
原子操作的优点编写简单；缺点是功能太简单，只能做计数操作，保护的东西太少。下面看一个实例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;static atomic_t v=ATOMIC_INIT(1);  
static int hello_open (struct inode *inode, struct file *filep)  
{  
    if(!atomic_dec_and_test(&amp;amp;v)) {   
        atomic_inc(&amp;amp;v);  
        return -EBUSY;  
    }  
    return 0;  
}    
static int hello_release (struct inode *inode, struct file *filep)  
{  
    atomic_inc(&amp;amp;v);  
    return 0;  
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自旋锁&lt;br&gt;
自旋锁（spin lock）是一个互斥设备，它只有两个值：“锁定”和“解锁”。它通常实现为某个整数值中的某个位。希望获得某个特定锁，需要代码测试相关的位。如果锁可用，则“锁定”被设置，而代码继续进入临界区；相反，**如果锁被其他人获得，则代码进入忙循环（而不是休眠，这也是自旋锁和一般锁的区别）**并重复检查这个锁，直到该锁可用为止，这就是自旋的过程。“测试并设置位”的操作必须是原子的，这样，即使多个线程在给定时间自旋，也只有一个线程可获得该锁。&lt;br&gt;
&lt;strong&gt;Linux自旋锁的操作&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;定义自旋锁&lt;br&gt;
&lt;code&gt;spinlock_t lock;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;初始化自旋锁&lt;br&gt;
&lt;code&gt;#define spin_lock_init(_lock)　　/* 该宏用于动态初始化自旋锁 lock */void spin_lock_init(spinlock_t *);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;获取自旋锁&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;void spin_lock(spinlock_t *lock);　　/* 该宏用于获得自旋锁 lock，如果 lock 未被加锁，它就会称为 持有者并立即返回，否者它将自旋在那里，知道该自旋锁的持有者释放 */
int spin_trylock(spinlock_t *lock);　　/* 该宏用于获得自旋锁 lock，如果 lock 未被加锁，它就会称为 持有者并返回真，否者立即返回假 */
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;释放自旋锁&lt;br&gt;
&lt;code&gt;void spin_unlock(spinlock_t *lock);　　/* 用于释放自旋锁，与 spin_lock 或 spin_trylock 配对使用*/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;自旋锁的使用&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/* 定义一个自旋锁 */
spinlock_t lock;
spin_lock_init(&amp;amp;lock);
spin_lock(&amp;amp;lock);　　/* 获取自旋锁，保护临界区 */
...
critical section　　/*临界区*/ 
...
spin_unlock(&amp;amp;lock);　　/* 解锁*/        
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自旋锁最初是为了在&lt;strong&gt;多处理器系统（SMP）使用而设计的&lt;/strong&gt;，但是只要考虑到并发问题，单处理器在运行可抢占内核时其行为就类似于SMP。因此，自旋锁对于SMP和单处理器可抢占内核都适用。可以想象，当一个处理器处于自旋状态时，它做不了任何有用的工作，因此自旋锁对于单处理器不可抢占内核没有意义，实际上，非抢占式的单处理器系统上自旋锁被实现为空操作，不做任何事情。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;br&gt;
1）&lt;strong&gt;自旋锁实际上是忙等锁&lt;/strong&gt;，因此只有在占用锁的时间极短的情况下，使用自旋锁才是合理的；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  2）**自旋锁可能导致系统死锁**。引发这个问题的常见情况是递归使用一个自旋锁；　　

  　　自旋锁导致死锁的实例】

  　　a) a进程拥有自旋锁，在**内核态阻塞**的，内核调度进程b，b也要或得自旋锁，b只能自旋，而此时抢占已经关闭了，a进程就不会调度到了，b进程永远自旋。

  　　b) 进程a拥有自旋锁，中断来了，cpu执行中断，中断处理函数也要获得锁访问共享资源，此时也获得不到锁，只能死锁。

  3）**自旋锁锁定期间不能调用任何可能引起进程调度的函数**。　
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;自旋锁的特性&lt;/strong&gt;：&lt;br&gt;
1. 被自旋锁保护的临界区代码执行时不能进入休眠；&lt;br&gt;
2. 被自旋锁保护的临界区代码执行时是不能被被其他中断中断；&lt;br&gt;
3. 被自旋锁保护的临界区代码执行时，内核不能被抢占。&lt;br&gt;
从这几个特性可以归纳出一个共性：被自旋锁保护的临界区代码执行时，它不能因为任何原因放弃处理器。&lt;br&gt;
&lt;strong&gt;读写自旋锁&lt;/strong&gt;&lt;br&gt;
读写自旋锁是一种比自旋锁粒度（保护范围）更小的锁机制，它保留了“自旋”的概念。在读写操作时，允许多个读执行单元；写操作最多有一个写进程，且读和写不能同时进行&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031036573.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;信号量&lt;br&gt;
信号量（semaphore）是用于保护临界区的一种常用方法，它的使用方式和自旋锁类似，只有得到信号量的进程才能执行临界区代码。但也与自旋锁有不同之处，对于&lt;strong&gt;获取不到信号量的执行序列将会进入休眠状态&lt;/strong&gt;而不是原地打转。
&lt;ul&gt;
&lt;li&gt;自旋锁和信号量的比较&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031110790.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;进程上下文和中断上下文的框架&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031115228.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;并发控制机制汇总&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031117927.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;114-采用dma方式进行串口通信如何解析数据才能提高吞吐量&#34;&gt;1.14 采用DMA方式进行串口通信，如何解析数据才能提高吞吐量&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/wuyongpeng0912/article/details/46634931&#34;&gt;HelloWuyp的博客DMA总结&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;直接存储访器问(DMA)&lt;/strong&gt;：用于在外设与存储器之间以及存储器与存储器之间进行高速数据传输。DMA传输过程的初始化和启动由CPU完成，传输过程由DMA控制器来执行，无需CPU参与，从而节省CPU资源，提高利用率。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031621123.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031637582.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DMA与CPU和外设的数据交换简图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031650683.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DMA传输和没有DMA传输区别&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有DMA传输&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;如果没有DMA,CPU传输数据还要以内核作为中转站，比如要将ADC采集的数据转移到到SRAM中，这个过程是这样的：&lt;/li&gt;
&lt;li&gt;内核通过&lt;strong&gt;DCode经过总线矩阵&lt;/strong&gt;协调，从获取AHB存储的外设ADC采集的数据，&lt;/li&gt;
&lt;li&gt;然后内核再通过DCode经过总线矩阵协调把数据存放到&lt;strong&gt;内存SRAM&lt;/strong&gt;中。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031629713.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;有DMA传输&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;DMA传输时外设对&lt;strong&gt;DMA控制器&lt;/strong&gt;发出请求。&lt;/li&gt;
&lt;li&gt;DMA控制器收到请求，触发DMA工作。&lt;/li&gt;
&lt;li&gt;DMA控制器从AHB外设获取ADC采集的数据，&lt;strong&gt;存储到DMA通道&lt;/strong&gt;中。&lt;/li&gt;
&lt;li&gt;DMA控制器的&lt;strong&gt;DMA总线&lt;/strong&gt;与&lt;strong&gt;总线矩阵&lt;/strong&gt;协调，使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中，这个数据的传输过程中，完全&lt;strong&gt;不需要内核的参与，也就是不需要CPU的参与&lt;/strong&gt;。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031633391.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如何解析数据提高吞吐量&lt;br&gt;
DMA传输方式&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;单字节传输方式；&lt;br&gt;
一次DMA只传输一个字节，效率很低。但是在传输过程中CPU有机会获取对总线的控制器。&lt;/li&gt;
&lt;li&gt;数据块传输方式；&lt;br&gt;
数据以数据块的方式进行传输。只要DREQ启动就会连续地传送数据块。一次请求传送一个数据块，效率高。在数据的传送期间，CPU长时间无法控制总线。&lt;/li&gt;
&lt;li&gt;请求传输方式；&lt;br&gt;
DREQ信号有效就连续传输数据，否则不能进行数据的传输。&lt;/li&gt;
&lt;li&gt;级联传输方式。&lt;br&gt;
用于通过多个DMA控制器级联以扩展通道。第一级只起优先权网络的作用，实际的操作由第二级芯片完成。还可由第二级到第三级等。（多个DMAC）&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;DMA的缺点&lt;br&gt;
DMA传送的&lt;strong&gt;优点是以增加系统硬件的复杂性和成本为代价的&lt;/strong&gt;，因为DMA是用硬件控制代替软件控制的。另外，DMA传送期间CPU被挂起，部分或完全失去对系统总线的控制，这可能会影响CPU对中断请求的及时响应与处理。因此，在一些小系统或速度要求不高、数据传输量不大的系统中，一般并不用DMA方式。&lt;br&gt;
因为DMA允许外设直接访问内存，从而形成&lt;strong&gt;对总线的独占&lt;/strong&gt;。这在实时性强的硬实时系统嵌入式开发中将会造成中断延时过长。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;115-c内联函数的缺点是什么&#34;&gt;1.15 c++内联函数的缺点是什么？&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/qq_35902025/article/details/127912415&#34;&gt;赵大宝字的内联函数&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;内联函数相关问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在这一节，我们先一口气回答前两节的所有问题，然后慢慢引出后面的话题。函数前面加上inline一定会有效果吗？&lt;br&gt;
答：不会，使用内联inline关键字修饰函数只是一种提示，编译器不一定认。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果不加inline就不是内联函数了吗？&lt;br&gt;
答：存在隐式内联，不用inline关键字，C++中在类内定义的所有函数都自动称为内联函数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联函数一定就会展开吗？&lt;br&gt;
答：其实和第一个问题类似，还是看编译器认不认。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在什么情况下内联函数会展开？&lt;br&gt;
答：首先需要满足有inline修饰或者是类中的定义的函数，然后再由编译器决定。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联函数的使用&lt;br&gt;
&lt;strong&gt;内联函数是定义在头文件还是源文件？&lt;/strong&gt;&lt;br&gt;
内联展开是在编译时进行的，只有链接的时候源文件之间才有关系。所以内联要想跨源文件必须把实现写在头文件里。如果一个内联函数会在多个源文件中被用到，那么必须把它定义在头文件中&lt;br&gt;
内联函数的定义不一定要跟声明放在一个头文件里面：定义可以放在一个单独的头文件中，里面需要给函数定义前加上inline 关键字，原因看下面第 2.点；然后声明 放在另一个头文件中，此文件include上一个头文件。这种用法 boost里很常见：优点1. 实现跟API分离封装。优点2. 可以解决有关inline函数的循环调用问题。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;隐式内联：&lt;br&gt;
如第三节说的C++中在类内定义的所有函数都自动称为内联函数，类的成员函数的定义直接写在类的声明中时，不需要inline关键字&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;stdio.h&amp;gt;

class Trace{
public:
    Trace()
    {
        noisy = 0;
    }
    void print(char *s)
    {
        if (noisy)
        {
            printf(&amp;quot;%s&amp;quot;, s);
        }
    }
    void on(){ noisy = 1; }
    void off(){ noisy = 0; }
private:
    int noisy;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;显示内联：&lt;br&gt;
需要使用inline关键字&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;stdio.h&amp;gt;

class Trace{
public:
    Trace()
    {
        noisy = 0;
    }
    void print(char *s); //类内没有显示声明
    void on(){ noisy = 1; }
    void off(){ noisy = 0; }
private:
    int noisy;
};
//类外显示定义
inline void Trace::print(char *s)
{
    if (noisy)
    {
        printf(&amp;quot;%s&amp;quot;, s);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联函数的重定义&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;什么是重定义？&lt;br&gt;
答：C/C++语法中，如果变量、函数在同一个工程中被多次定义，链接期间会报类似“对 xxx 多重定义”的错误。&lt;br&gt;
当内联函数的声明和定义分别在头文件和源文件中，并且在其他文件中被调用时，链接期间编译器会报“对 xxx 未定义的引用”错误。内联函数如果会在多处被调用，则需要将函数的定义写在头文件中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;​为什么inline关键字修饰的函数定义在头文件中（函数可能会被多次定义），编译器不会报“对 xxx 多重定义”的错误呢？&lt;br&gt;
答：编译器对被inline修饰的函数做了特殊处理，inline起到了内联的作用；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;inline为什么能起作用？&lt;br&gt;
答：因为inline是一个弱符号；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;什么是弱符号？(反编译后.weak确定)&lt;br&gt;
答：在C语言中，编译器默认函数和初始化了的全局变量为强符号（Strong Symbol），未初始化的全局变量为弱符号（Weak Symbol）。强符号之所以强，是因为它们拥有确切的数据，变量有值，函数有函数体；弱符号之所以弱，是因为它们还未被初始化，没有确切的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;链接器会按照如下的规则处理被多次定义的强符号和弱符号：&lt;br&gt;
1) 不允许强符号被多次定义，也即不同的目标文件中不能有同名的强符号；如果有多个强符号，那么链接器会报符号重复定义错误。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  2) 如果一个符号在某个目标文件中是强符号，在其他文件中是弱符号，那么选择强符号。

  3) 如果一个符号在所有的目标文件中都是弱符号，那么选择其中占用空间最大的一个。
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联函数与宏&lt;br&gt;
在C程序中，可以用&lt;strong&gt;宏代码提高执行效率&lt;/strong&gt;。宏代码本身不是函数，但使用起来象函数。预处理器用&lt;strong&gt;复制宏代码的方式代替函数调用&lt;/strong&gt;，省去了参数压栈、生成汇编语言的CALL调用、 返回参数、执行return等过程，从而提高了速度。&lt;br&gt;
使用宏代码最大的缺点是容易出错，预处理器在复制宏代码时常常产生意想不到的&lt;strong&gt;边际效应&lt;/strong&gt;。宏看起来像函数调用，但没有参数类型及返回值，实际会有隐藏的难以发现的问题，例如：执行ans = MyAdd(2, 3)&lt;em&gt;2时会返回2+3&lt;/em&gt;2。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联的局限性&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306012253063.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联的优缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;inline定义的内联函数，函数代码被放入符号表中，在使用时进行替换（像宏一样展开），效率很高&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;类的内联函数也是函数。编绎器在调用一个内联函数，首先会检查参数问题，保证调用正确，像对待真正函数一样，消除了隐患及局限性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;inline可以作为类的成员函数，也可以使用所在类的保护成员及私有成员。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;缺点&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;内联函数以复制为代价，活动产函数开销&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果函数的代码较长，使用内联将消耗过多内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果函数体内有循环，那么执行函数代码时间比调用开销大。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;116-是否做个i2c从机设备开发没有&#34;&gt;1.16 是否做个i2c从机设备开发（没有）&lt;/h2&gt;
&lt;h2 id=&#34;117-异步i2c有接触过吗&#34;&gt;1.17 异步i2c有接触过吗？&lt;/h2&gt;
&lt;h2 id=&#34;118-看门狗多线程如何喂狗&#34;&gt;1.18 看门狗：多线程如何喂狗？&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;喂狗&lt;/strong&gt;：就是及时清除看门狗数据，避免正常情况下重启程序。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;定时器学习&lt;br&gt;
参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1MY4y1u7eY/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统定时器SysTick（滴答定时器）&lt;/li&gt;
&lt;li&gt;基础定时器&lt;/li&gt;
&lt;li&gt;专用定时器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;看门狗的由来&lt;br&gt;
由于单片机在运行过程经常会遭到外部电磁场的干扰，就可能会出现程序“跑飞”，如：出现数据的缺失，导致寄存器中的数据发生变化、程序指针&lt;code&gt;PC&lt;/code&gt;指向非法地址.......，为了提高程序的可靠性，就提供了看门狗外设，在程序出现由软件或硬件导致的未知问题，利用看门狗外设对&lt;strong&gt;芯片进行复位&lt;/strong&gt; ，相当于重新运行程序。&lt;br&gt;
如果程序没有任何问题，则可以&lt;strong&gt;按时喂狗&lt;/strong&gt;，&lt;strong&gt;就不会让芯片复位&lt;/strong&gt;，一旦程序出现问题导致&lt;strong&gt;无法喂狗&lt;/strong&gt;，则&lt;strong&gt;看门狗会自动复位程序&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;看门狗的分类&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;独立看门狗&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031815182.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031831984.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;看门狗代码编写&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;void IWDG_Init(void){
    //1. 判断系统复位是否由看门狗导致
    if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET){
        printf(&amp;quot;System Reset is by IWDG\n&amp;quot;);
        RCC_ClearFlag();        //清除复位标志，看门狗中断会置为1
    }else{
        printf(&amp;quot;System Reset is by User\n&amp;quot;);
        //不用清除复位，默认为0，手动复位的值也是0
    }
    //2. 解除写保护
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
    //3. 设置分频，和喂狗时间  32khz / 256 = 125
    IWDG_SetPrescaler(IWDG_Prescaler_256);
    IWDG_SetReload(125-1);
    //4. 重载计数值（或理解为将计数值刷入寄存器中）--- 喂狗代码
    IWDG_ReloadCounter();
    //5. 启动看门狗
    IWDG_Enable();

}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;窗口看门狗&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;软件看门狗&lt;br&gt;
由CPU时钟作为看门狗定时器，不能检测到CPU的异常&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;硬件看门狗&lt;br&gt;
由独立低速定时作为看门狗定时器，可以检测所有CPU是否正常运行；且任何一个CPU都可以喂硬狗，当在一定时间内没有核喂狗，触发硬狗复位&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多线程喂狗&lt;br&gt;
参考链接：&lt;a href=&#34;https://blog.csdn.net/little_grapes/article/details/126843059&#34;&gt;xiaobaibai_2021的博客&lt;/a&gt;&lt;br&gt;
参考链接：&lt;a href=&#34;https://www.sohu.com/a/239171102_505886&#34;&gt;搜狐网站多线程看门狗&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多线程喂狗示意图&lt;/strong&gt;：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306031928742.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;119-三极管的四种方式&#34;&gt;1.19 三极管的四种方式&lt;/h2&gt;
&lt;h2 id=&#34;120-中断时钟存储等&#34;&gt;1.20 中断，时钟，存储等&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;扇区：512byte(字节)&lt;/li&gt;
&lt;li&gt;时钟频率：&lt;/li&gt;
&lt;li&gt;存储：&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;121-线程&#34;&gt;1.21 线程&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;pthread_join方法(参考链接：&lt;a href=&#34;https://blog.csdn.net/yzy1103203312/article/details/80849831&#34;&gt;多线程pthread_join()的两种作用&lt;/a&gt;)&lt;br&gt;
&amp;gt; 1. 用于等待其他线程结束：当调用 pthread_join() 时，当前线程会处于阻塞状态，直到被调用的线程结束后，当前线程才会重新开始执行。&lt;br&gt;
&amp;gt; 2. 对线程的资源进行回收：如果一个线程是非分离的（默认情况下创建的线程都是非分离）并且没有对该线程使用 pthread_join() 的话，该线程结束后并不会释放其内存空间，这会导致该线程变成了“僵尸线程”。&lt;br&gt;
线程的分离状态决定一个线程以什么样的方式来终止自己&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分离线程【detached】和非分离线程【joinable】&lt;br&gt;
&lt;strong&gt;非分离的线程&lt;/strong&gt;能够被其他线程收回其资源和杀死；在被其他线程回收之前，它的存储器资源（如栈）是不释放的【默认状态】&lt;br&gt;
&lt;strong&gt;分离的线程&lt;/strong&gt;是不能被其他线程回收或杀死的，它的存储器资源在它终止时由系统自动释放&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;122-仿函数&#34;&gt;1.22 仿函数&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/toby54king/article/details/105103111&#34;&gt;ISmileLi博客的仿函数&lt;/a&gt;&lt;br&gt;
参考链接：&lt;a href=&#34;https://blog.csdn.net/K346K346/article/details/82818801&#34;&gt;恋喵大鲤鱼博客的仿函数&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;123-explicit函数介绍&#34;&gt;1.23 explicit函数介绍&lt;/h2&gt;
&lt;p&gt;作用：explicit构造函数是用来防止隐式转换的&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

// explicit函数的介绍!!!

// explicit函数的作用：explicit构造函数是用来防止隐式转换的
class Test1{
    public:
        Test1(int n){  // 普通隐式的构造函数
            num = n;
        }
    private:
        int num;
};

class Test2{
    public:
        explicit Test2(int n){   //explicit(显式)构造函数
            num = n;
        }
    private:
        int num;
};

int main(){
    Test1 t1 = 12;  // 隐式调用其构造函数,成功
    // Test2 t2 = 12;  编译错误,不能隐式调用其构造函数
    Test2 t3(12);  //  显式调用成功
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test1的构造函数带一个int型的参数，会隐式转换成调用Test1的这个构造函数。而Test2的构造函数被声明为explicit（显式），这表示不能通过隐式转换来调用这个构造函数，因此Test2 t2 = 12会出现编译错误。普通构造函数能够被隐式调用,而explicit构造函数只能被显式调用。&lt;/p&gt;
&lt;h2 id=&#34;124-static关键cc&#34;&gt;1.24 static关键c/c++&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/guotianqing/article/details/79828100&#34;&gt;guotianqing的博客&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;125-用户和内核态&#34;&gt;1.25 用户和内核态&lt;/h2&gt;
&lt;h3 id=&#34;1251用户态和内核态概述&#34;&gt;1.25.1用户态和内核态概述&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;用户态：提供应用程序运行的空间，为了使应用程序访问到内核管理的资源，例如CPU，内存，I/O等。&lt;br&gt;
内核态：本质是内核，一种特殊的软件程序，用于控制计算机的硬件资源，例如协调CPU资源，分配内存资源，并且提供稳定的环境供应用程序运行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;用户态只能受限的访问内存, 且不允许访问外设(硬盘、网卡等)；内核态CPU可以访问内存所有数据, 包括外设，且可以将自己从一个程序切换到另一个程序。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;系统调用&lt;/strong&gt;：操作系统对外会表现为一个整体，但是会暴露自己的部分接口来供上层开发使用，这部分由操作系统提供的接口，叫做系统调用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;库函数&lt;/strong&gt;：库函数实际上就是对系统调用接口的封装，提供简单的基本接口给用户&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shell&lt;/strong&gt;：命令行，为了方便用户和系统交互。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;1252-用户态与内核态的切换&#34;&gt;1.25.2 用户态与内核态的切换&lt;/h3&gt;
&lt;p&gt;用户程序跑在用户态下，但是如果需要执行一些操作例如申请内存，网络读写时，自己的权限不够，就需要转换到内核态去让内核的code帮忙干一些事情，这样就存在用户态和内核态切换的过程，例如：&lt;strong&gt;C库接口malloc申请动态内存&lt;/strong&gt;，malloc的实现内部&lt;strong&gt;可能&lt;/strong&gt;会调用 系统调用brk()来分配内存。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt;   上述例子提到的malloc的实现内部为什么是可能会进行系统调用而不是一定会进行系统调用呢？
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt;   malloc是用于用户空间堆扩展的函数接口。该函数是C库，属于封装了相关系统调用（brk()）的glibc库函数，而不是系统调用（系统可没有sys_malloc()），如果谈及malloc函数涉及的系统内核的那些操作，那么总体可以分为用户空间层面和内核空间层面来讨论：
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;用户层面：每当进程调用malloc，首先会在该堆缓冲区寻找足够大小的内存块分配给进程（选择缓冲区中的那个块就有首次命中和最佳命中两种算法）。如果free_chunk_list已无法满足需求的chunk时，那么malloc会通过调用系统调用brk()将进程空间的堆进行扩展，在新扩展的堆空间上建立一个新的chunk并加入到空闲队列中，这个过程相当于进程想向系统批量申请一块内存&lt;br&gt;
内核层面：用户层面中如果malloc的空闲chunk列表无法满足用户的需求，那么就要通过sys_brk()进行堆的扩展，这时候才真正算得上进入内核空间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;那么如何从用户态切换到内核态呢？有以下三种方式&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;系统调用&lt;/strong&gt;：是用户态进程主动要求切换到内核态的一种方式，用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作。例如上面的例子、还比如fork()实际上就是执行了一个创建新进程的系统调用。(其实系统调用本身就是中断，是软件中断)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常&lt;/strong&gt;：如果当前进程运行在用户态，如果这个时候发生了异常事件，就会触发切换。例如：缺页异常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外设中断&lt;/strong&gt;：当外设完成用户的请求时，会向CPU发送中断信号，此时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序，如果先前执行的指令是用户态下的程序，那么这个转换的过程自然也就发生了由用户态到内核态的切换。例：如硬盘读写操作完成后，系统会切换到硬盘读写的中断处理程序中执行后续操作。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;用户态和内核态之间切换调度的成本很高，原因是什么？&lt;/strong&gt;&lt;br&gt;
答：Linux下每个进程的栈有两个，一个是用户态栈，一个是内核态栈。在从用户态栈进入内核的时候，进行执行栈的转换，需要保存用户态的寄存器，在内核态返回用户态的时候会恢复这些寄存器的内容，相对而言这是一个很大的开销且耗时。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;操作系统通过中断来从用户态切换到内核态，内核态可以直接切换到用户态&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;126-linux系统的惊群效应&#34;&gt;1.26 Linux系统的惊群效应&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://www.programminghunter.com/article/74692326128/&#34;&gt;编程猎人---Linux惊群效应&lt;/a&gt;&lt;/p&gt;
">嵌入式---知识盲点</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/9_Fn4Xz4p/"" data-c="
          &lt;h1 id=&#34;1-易混淆知识&#34;&gt;1. 易混淆知识&lt;/h1&gt;
&lt;h2 id=&#34;11-基础语法&#34;&gt;1.1 基础语法&lt;/h2&gt;
&lt;h3 id=&#34;111-a1a1a1a1a1的区别&#34;&gt;1.1.1 &lt;code&gt;a+1，*(a+1)，*(&amp;amp;a+1)，&amp;amp;a+1，*(*(&amp;amp;a+1))&lt;/code&gt;的区别&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a+1&lt;/code&gt;：就是数组首地址加上一个元素所占的地址大小，结果是一个&lt;strong&gt;地址&lt;/strong&gt;，这里int是4字节，则就是&lt;code&gt;1x4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*(a+1)&lt;/code&gt;：代表的是数组的第一个元素的&lt;strong&gt;值&lt;/strong&gt;，不再是地址&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*(&amp;amp;a+1)&lt;/code&gt;：将&amp;amp;a+1的地址取出来，这值是&lt;code&gt;地址的值&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;a+1&lt;/code&gt;：代表的是加上&lt;strong&gt;整个数组的大小&lt;/strong&gt;，例如：数组长度为3，则&amp;amp;a+1表示指向数组最后一个元素的下一个地址开始处，即这里的&lt;code&gt;+1&lt;/code&gt;代表的是加上&lt;code&gt;3x4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*(*(&amp;amp;a+1)-1)&lt;/code&gt;：取出&lt;code&gt;&amp;amp;a+1&lt;/code&gt;地址的前一个元素的值。（即数组的最后一个元素的值）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;112-scanf函数的返回值&#34;&gt;1.1.2 scanf()函数的返回值&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;scanf 函数是有返回值的，它的返回值可以分成三种情况&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;正整数，表示正确输入参数的个数。&lt;br&gt;
例如执行 scanf(“%d %d”, &amp;amp;a, &amp;amp;b);&lt;br&gt;
如果用户输入”3 4”，可以正确输入，返回2（正确输入了两个变量）；&lt;br&gt;
如果用户输入”3,4”，可以正确输入a，无法输入b，返回1（正确输入了一个变量）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;0，表示用户的输入不匹配，无法正确输入任何值。如上例，用户如果输入”,3 4”，返回0。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;EOF，这是在 stdio.h 里面定义的常量（通常值为-1），表示输入流已经结束。在Windows下，用户按下CTRL+Z（会看到一个^Z字符）再按下回车（可能需要重复2次），就表示输入结束；Linux/Unix下使用CTRL+D表示输入结束。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;scanf 的格式串里面，为什么%s 和 %c 中间需要空格呢？&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;while (scanf(&amp;quot;%s %c %c&amp;quot;, str, &amp;amp;oldchar, &amp;amp;newchar) == 3)
/* 或!= EOF , 但前者更好 */
{
    ; //处理
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;因为如果没空格的话。。。oldchar输入的就是空格了= =.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;补充&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;printf 的返回值是输出的字符数，例如，printf(“1234”)的返回值是4，而printf(“1234\n”)的返回值是5。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;113-const-和static区别&#34;&gt;1.1.3 const 和static区别&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;常量（const 类型）&lt;/strong&gt;&lt;br&gt;
const类型的值是不能被修改的，但是，这个不能修改，意思是const 修饰的部分是不能被修改的。典型的应用是如下的两种：
&lt;ul&gt;
&lt;li&gt;const     int*     p; 这个声明的意思是：p 是一个指针，是指向const int 的一个指针。（p is a pointer, point to a const int）也就是说，p这个&lt;strong&gt;地址是可以改变&lt;/strong&gt;的，但是，不管地址如何变，其&lt;strong&gt;指向的值是不变&lt;/strong&gt;的。p = p + 1 是对的，但是 *p是不合法的，不能改变*p的值。&lt;/li&gt;
&lt;li&gt;int*    const      p; 同样是定义一个指针，意思是：p是一个const的int类型的指针。（p is a const pointer to int）也就是说，p是一个地址，但是这个&lt;strong&gt;地址不能被改变&lt;/strong&gt;，但是里面的&lt;strong&gt;值 *p 是可以改变&lt;/strong&gt;的。p = p+1 是错误的，因为p已经固定了，但是*p是合法的。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;const int* p：等价于（const int)* p，即：p是一个指针，指向（const int）类型&lt;br&gt;
int* const p：等价于int* (const p)，即：p是一个常量指针（const p），指向int类型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态变量（static）&lt;/strong&gt;&lt;br&gt;
静态变量分为全局的静态变量和局部的静态变量。静态变量的&lt;strong&gt;值是可以改变&lt;/strong&gt;的！
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;全局静态变量&lt;/strong&gt;：这个比较简单，作用域内的所有函数都可以调用，一般可以用来做多线程程序的控制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局部静态变量&lt;/strong&gt;：在这个例子中，一共调用了func函数三次，每调用一次，i的值都会改变一次。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;114-结构体内存对齐和修改默认对齐数&#34;&gt;1.1.4 结构体内存对齐和修改默认对齐数&lt;/h3&gt;
&lt;p&gt;参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1Vm4y1r7jY?p=137&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;鹏哥C语言&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;结构体内存对齐&lt;br&gt;
对齐规则：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;第一个成员在结构体变量偏移量为0的地址处。&lt;/li&gt;
&lt;li&gt;其他成员变量要对齐到某个数字（对齐数）的整数倍的地址处。&lt;br&gt;
&lt;strong&gt;对齐数&lt;/strong&gt; = 编译器默认的对齐数 与 该成员大小的 &lt;strong&gt;较小值&lt;/strong&gt;。如：&lt;code&gt;vs中默认值为8&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;结构体总体大小为最大对齐数（每个成员变量都有一个对齐数）的整数倍。&lt;/li&gt;
&lt;li&gt;如果嵌套了结构体的情况，嵌套的结构体对齐到&lt;strong&gt;自己的最大对齐数的整数倍处&lt;/strong&gt;，结构体的整体大小就是所有最大对齐数（含嵌套结构体的对齐数）的整数倍。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;为啥存在内存对齐？&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306080029910.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改默认对齐数&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306080847474.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;115-结构体和共用体区别&#34;&gt;1.1.5 结构体和共用体区别&lt;/h3&gt;
&lt;p&gt;参考博客链接：&lt;a href=&#34;https://blog.csdn.net/qq_40305944/article/details/116270391&#34;&gt;嵌入式小刘-共用体和结构体的区别&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;共用体的特点&lt;br&gt;
1）使用共用体变量的目的是希望用同一个内存段存放几种不同类型的数据，但请注意，在&lt;strong&gt;每一个瞬间只能存放其中一种&lt;/strong&gt;，而不是同时存放几种；&lt;br&gt;
2）能够&lt;strong&gt;访问&lt;/strong&gt;的是共用体变量中&lt;strong&gt;最后一次被赋值的成员&lt;/strong&gt;，在对一个新的成员赋值后原有的成员就失去作用。&lt;br&gt;
3）共用体变量的地址和它各成员的地址都是&lt;strong&gt;同一个地址&lt;/strong&gt;；&lt;br&gt;
4）&lt;strong&gt;不能对共用体变量名赋值&lt;/strong&gt;；不能企图引用变量名来得到一个值；不能在定义共用体变量时对它初始化；不能用共用体变量名作为函数参数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;共用体变量的赋值&lt;br&gt;
参考博客链接：&lt;a href=&#34;https://blog.csdn.net/qq_43589855/article/details/84578455&#34;&gt;KingSF5-共用体赋值&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;116-文件打开的方式&#34;&gt;1.1.6 文件打开的方式&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文件使用方式&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;th&gt;如果指定的文件不存在&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;r（只读）&lt;/td&gt;
&lt;td&gt;读取一个已经存在的文本文件&lt;/td&gt;
&lt;td&gt;出错&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;w（只写）&lt;/td&gt;
&lt;td&gt;打开一个文本文件，输出数据，若文件存在则文件长度清为0，即该文件内容会消失&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a （追加）&lt;/td&gt;
&lt;td&gt;向文本文件末尾添加数据，原来文件中的数据保留，新的数据添加到文件为，原文件EOF保留&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rb（只读）&lt;/td&gt;
&lt;td&gt;读取一个二进制文件&lt;/td&gt;
&lt;td&gt;出错&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wb（只写）&lt;/td&gt;
&lt;td&gt;打开一个二进制文件，输出数据，若文件存在则文件长度清为0，即该文件内容会消失&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ab （追加）&lt;/td&gt;
&lt;td&gt;向二进制文件尾添加数据&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r+ （读写）&lt;/td&gt;
&lt;td&gt;对一个文本文件进行读写操作&lt;/td&gt;
&lt;td&gt;出错&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;w+ （读写）&lt;/td&gt;
&lt;td&gt;对一个文本文件进行读写操作，若文件存在则文件长度清为0，即该文件内容会消失&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a+（读写）&lt;/td&gt;
&lt;td&gt;向文本文件末尾添加数据，原来文件中的数据保留，新的数据添加到文件尾，原文件EOF不保留&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rb+ （读写）&lt;/td&gt;
&lt;td&gt;读写一个二进制文件&lt;/td&gt;
&lt;td&gt;出错&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wb+ （读写）&lt;/td&gt;
&lt;td&gt;对一个二进制文件进行读写操作，若文件存在则文件长度清为0，即该文件内容会消失&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ab+（读写）&lt;/td&gt;
&lt;td&gt;向二进制文件末尾添加数据，原来文件中的数据保留，新的数据添加到文件尾&lt;/td&gt;
&lt;td&gt;建立新文件&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;12-经典代码片段&#34;&gt;1.2 经典代码片段&lt;/h2&gt;
&lt;h3 id=&#34;121-统计二进制数中1的个数&#34;&gt;1.2.1 统计二进制数中&lt;code&gt;1&lt;/code&gt;的个数&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;int countOne(int x){
    int count = 0;
    while (x){
        count++;
        x = x &amp;amp; (x-1);
    }
    return count;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;122-编程代码大小端判断&#34;&gt;1.2.2 编程代码大小端判断&lt;/h3&gt;
&lt;p&gt;参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1Vm4y1r7jY?p=146&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;比特鹏---联合&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;# include&amp;lt;stdio.h&amp;gt;
//方式一
int  check_sys(){
    int a = 1;
    return *(char*)&amp;amp;a;  //将a转为char类型指针，只能取一个字节，然后在去值返回
}
//方式二：共用体
int check_sys(){
    union {
        char c;
        int i;
    }
    u.i = 1;
    return u.c;
}

int main(){
    //int a = 1; //0x 00 00 00 01
    //低------&amp;gt;高
    //01 00 00 00 -- 小端
    //00 00 00 01 -- 大端
    int ret = check_sys();
    if(ret == 1){
        printf(&amp;quot;小端\n&amp;quot;);
    }else{
        printf(&amp;quot;大端\n&amp;quot;);
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;共用体字节对齐&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;char arr[5] 等价于 char a,b,c,d,e;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202306101906110.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;13-c语言内存分区&#34;&gt;1.3 c语言内存分区&lt;/h2&gt;
&lt;p&gt;内存分区示意图，详细请参考笔记：&lt;a href=&#34;siyuan://blocks/20230526130845-mf1blib&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305270008000.png&#34; alt=&#34;c语言内存分区图&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
">C基础知识</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/ttHxL7o9x/"" data-c="
          &lt;h1 id=&#34;1-函数&#34;&gt;1. 函数&lt;/h1&gt;
&lt;p&gt;c++的类函数可以在类的的外面通过&lt;code&gt;返回类型 类名::方法名(){}&lt;/code&gt;的方式进行声明定义&lt;/p&gt;
&lt;h2 id=&#34;11-构造函数&#34;&gt;1.1 构造函数&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;类的构造函数是类的一种特殊的成员函数，它会在每次创建类的新对象时执行。&lt;br&gt;
构造函数的名称与类的名称是完全相同的，并且不会返回任何类型，也不会返回 void。构造函数可用于&lt;br&gt;
为某些成员变量设置初始值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;代码示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();  // 这是构造函数
 
   private:
      double length;
};
 
// 成员函数定义，包括构造函数
Line::Line(void)
{
    cout &amp;lt;&amp;lt; &amp;quot;Object is being created&amp;quot; &amp;lt;&amp;lt; endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout &amp;lt;&amp;lt; &amp;quot;Length of line : &amp;quot; &amp;lt;&amp;lt; line.getLength() &amp;lt;&amp;lt;endl;
 
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;12-方法重载和运算符重载&#34;&gt;1.2 方法重载和运算符重载&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;C++ 允许在同一作用域中的某个函数和运算符指定多个定义，分别称为函数重载和运算符重载。&lt;br&gt;
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明，但是它们的参数列表和定义（实现）不相同。&lt;br&gt;
当您调用一个重载函数或重载运算符时，编译器通过把您所使用的参数类型与定义中的参数类型进行比较，决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程，称为重载决策。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;C++ 中的函数重载&lt;/strong&gt;&lt;br&gt;
在同一个作用域内，可以声明几个功能类似的同名函数，但是这些同名函数的形式参数（指参数的个数、类型或者顺序）必须不同。不能仅通过返回类型的不同来重载函数。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
 
class printData
{
   public:
      void print(int i) {
        cout &amp;lt;&amp;lt; &amp;quot;整数为: &amp;quot; &amp;lt;&amp;lt; i &amp;lt;&amp;lt; endl;
      }
 
      void print(double  f) {
        cout &amp;lt;&amp;lt; &amp;quot;浮点数为: &amp;quot; &amp;lt;&amp;lt; f &amp;lt;&amp;lt; endl;
      }
 
      void print(char c[]) {
        cout &amp;lt;&amp;lt; &amp;quot;字符串为: &amp;quot; &amp;lt;&amp;lt; c &amp;lt;&amp;lt; endl;
      }
};
 
int main(void)
{
   printData pd;
 
   // 输出整数
   pd.print(5);
   // 输出浮点数
   pd.print(500.263);
   // 输出字符串
   char c[] = &amp;quot;Hello C++&amp;quot;;
   pd.print(c);
 
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;C++ 中的运算符重载&lt;/strong&gt;&lt;br&gt;
可以重定义或重载大部分 C++ 内置的运算符。这样，您就能使用自定义类型的运算符。&lt;/p&gt;
&lt;p&gt;重载的运算符是带有特殊名称的函数，函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样，重载运算符有一个返回类型和一个参数列表。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Box operator+(const Box&amp;amp;);&lt;br&gt;
声明加法运算符用于把两个 Box 对象相加，返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数，那么我们需要为每次操作传递两个参数，如下所示：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Box operator+(const Box&amp;amp;, const Box&amp;amp;);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;运算符重载的基本格式&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt; 返回值类型 类名::operator重载的运算符(参数表)
   {
   ……
   }

operator是关键字，它与重载的运算符一起构成函数名。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
 
class Box
{
   public:
 
      double getVolume(void)
      {
         return length * breadth * height;
      }
      void setLength( double len )
      {
          length = len;
      }
 
      void setBreadth( double bre )
      {
          breadth = bre;
      }
 
      void setHeight( double hei )
      {
          height = hei;
      }
      // 重载 + 运算符，用于把两个 Box 对象相加
      Box operator+(const Box&amp;amp; b)
      {
         Box box;
         box.length = this-&amp;gt;length + b.length;
         box.breadth = this-&amp;gt;breadth + b.breadth;
         box.height = this-&amp;gt;height + b.height;
         return box;
      }
   private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
};
// 程序的主函数
int main( )
{
   Box Box1;                // 声明 Box1，类型为 Box
   Box Box2;                // 声明 Box2，类型为 Box
   Box Box3;                // 声明 Box3，类型为 Box
   double volume = 0.0;     // 把体积存储在该变量中
 
   // Box1 详述
   Box1.setLength(6.0); 
   Box1.setBreadth(7.0); 
   Box1.setHeight(5.0);
 
   // Box2 详述
   Box2.setLength(12.0); 
   Box2.setBreadth(13.0); 
   Box2.setHeight(10.0);
 
   // Box1 的体积
   volume = Box1.getVolume();
   cout &amp;lt;&amp;lt; &amp;quot;Volume of Box1 : &amp;quot; &amp;lt;&amp;lt; volume &amp;lt;&amp;lt;endl;
 
   // Box2 的体积
   volume = Box2.getVolume();
   cout &amp;lt;&amp;lt; &amp;quot;Volume of Box2 : &amp;quot; &amp;lt;&amp;lt; volume &amp;lt;&amp;lt;endl;
 
   // 把两个对象相加，得到 Box3
   Box3 = Box1 + Box2;
 
   // Box3 的体积
   volume = Box3.getVolume();
   cout &amp;lt;&amp;lt; &amp;quot;Volume of Box3 : &amp;quot; &amp;lt;&amp;lt; volume &amp;lt;&amp;lt;endl;
 
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当上面的代码被编译和执行时，它会产生下列结果：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Volume of Box1 : 210&lt;br&gt;
Volume of Box2 : 1560&lt;br&gt;
Volume of Box3 : 5400&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;可重载运算符&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305251728049.png&#34; alt=&#34;可重载运算符&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;不可重载运算符
&lt;ul&gt;
&lt;li&gt;.：成员访问运算符&lt;/li&gt;
&lt;li&gt;.&lt;em&gt;, -&amp;gt;&lt;/em&gt;：成员指针访问运算符&lt;/li&gt;
&lt;li&gt;::：域运算符&lt;/li&gt;
&lt;li&gt;sizeof：长度运算符&lt;/li&gt;
&lt;li&gt;?:：条件运算符&lt;/li&gt;
&lt;li&gt;#： 预处理符号&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;13-析构函数&#34;&gt;1.3 析构函数&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;类的析构函数是类的一种特殊的成员函数，它会在每次删除所创建的对象时执行。&lt;br&gt;
析构函数的名称与类的名称是完全相同的，只是在前面加了个波浪号（~）作为前缀，它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序（比如关闭文件、释放内存等）前释放资源。&lt;br&gt;
&lt;strong&gt;补充：&lt;/strong&gt; 析构函数与java中finalize函数作用相类似，可以充当做垃圾回收机制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;代码示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();   // 这是构造函数声明
      ~Line();  // 这是析构函数声明
 
   private:
      double length;
};
 
// 成员函数定义，包括构造函数
Line::Line(void)
{
    cout &amp;lt;&amp;lt; &amp;quot;Object is being created&amp;quot; &amp;lt;&amp;lt; endl;
}
Line::~Line(void)
{
    cout &amp;lt;&amp;lt; &amp;quot;Object is being deleted&amp;quot; &amp;lt;&amp;lt; endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout &amp;lt;&amp;lt; &amp;quot;Length of line : &amp;quot; &amp;lt;&amp;lt; line.getLength() &amp;lt;&amp;lt;endl;
 
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;14-虚函数和纯虚函数&#34;&gt;1.4 虚函数和纯虚函数&lt;/h2&gt;
&lt;p&gt;虚函数和纯虚函数的主要作用是为了实现&lt;code&gt;多态&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;定义一个函数为虚函数，不代表函数为不被实现的函数。&lt;br&gt;
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。&lt;br&gt;
定义一个函数为纯虚函数，才代表函数没有被实现。&lt;br&gt;
定义纯虚函数是为了实现一个接口，起到一个规范的作用，规范继承这个类的程序员必须实现这个函数。&lt;br&gt;
&lt;strong&gt;补充：&lt;/strong&gt; &lt;code&gt;虚函数&lt;/code&gt;允许基类通过指针调用子类中重写的这个虚函数（这个方法定义在父类&lt;code&gt;virtual type function()&lt;/code&gt;），&lt;code&gt;纯虚函数&lt;/code&gt;类似java中的抽象方法，子类必须重新此方法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虚代码示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;class A
{
public:
    virtual void foo()
    {
        cout&amp;lt;&amp;lt;&amp;quot;A::foo() is called&amp;quot;&amp;lt;&amp;lt;endl;
    }
};
class B:public A
{
public:
    void foo()
    {
        cout&amp;lt;&amp;lt;&amp;quot;B::foo() is called&amp;quot;&amp;lt;&amp;lt;endl;
    }
};
int main(void)
{
    A *a = new B();
    a-&amp;gt;foo();   // 在这里，a虽然是指向A的指针，但是被调用的函数(foo)却是B的!
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;纯虚函数定义代码示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;virtual void funtion1()=0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;15-友元函数&#34;&gt;1.5 友元函数&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;类的友元函数是定义在类外部，但有权访问类的所有私有（private）成员和保护（protected）成员。尽管友元函数的原型有在类的定义中出现过，但是友元函数并不是成员函数。&lt;br&gt;
友元可以是一个函数，该函数被称为友元函数；友元也可以是一个类，该类被称为友元类，在这种情况下，整个类及其所有成员都是友元。&lt;br&gt;
如果要声明函数为一个类的友元，需要在类定义中该函数原型前使用关键字 friend。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );
   void setWidth( double wid );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元，需要在类 ClassOne 的定义中放置如下声明：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;friend class ClassTwo;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码示例&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
 
using namespace std;
 
class Box
{
   double width;
public:
   friend void printWidth( Box box );
   void setWidth( double wid );
};
 
// 成员函数定义
void Box::setWidth( double wid )
{
    width = wid;
}
 
// 请注意：printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
   /* 因为 printWidth() 是 Box 的友元，它可以直接访问该类的任何成员 */
   cout &amp;lt;&amp;lt; &amp;quot;Width of box : &amp;quot; &amp;lt;&amp;lt; box.width &amp;lt;&amp;lt;endl;
}
 
// 程序的主函数
int main( )
{
   Box box;
 
   // 使用成员函数设置宽度
   box.setWidth(10.0);
   
   // 使用友元函数输出宽度
   printWidth( box );
 
   return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;2-容器stl标准模板库&#34;&gt;2. 容器（STL：标准模板库）&lt;/h1&gt;
&lt;p&gt;容器是标准模板库的一个子集&lt;/p&gt;
&lt;h2 id=&#34;21-allocator&#34;&gt;2.1 allocator&lt;/h2&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/qingdujun/article/details/85224771&#34;&gt;qingdujun---浅谈C++ allocator内存管理（对比new的局限性）&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;简介：allocator类是C++的一个模板，它提供类型化的内存分配以及对象的分配和撤销。&lt;br&gt;
详介：allocator类定义在头文件memory中。它帮助我们将内存分配和对象构造分离开来。对于内存管理，我们大都习惯使用new 和 delete，毕竟这语句简单好记。而allocator类的用武之地体现在，你想更细致得去管理你的内存，减少浪费。下面举个例子。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
int main()
{
   //当我们想输入一些单词到一个string的数组
    int n = 1000;//因为不确定，就给多点
    string *p = new string[n];
    string *q = p;
    string word;
    while (cin &amp;gt;&amp;gt; word) {  //输入
        *p++ = word;
    }
    while (q != p) {       //输出
        cout &amp;lt;&amp;lt; *q++ &amp;lt;&amp;lt; &amp;quot; &amp;quot; &amp;lt;&amp;lt; flush;
    }
    cout &amp;lt;&amp;lt; endl;
    delete []p;
    q = nullptr;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;c++中allocator的代码示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
int main()
{
    int n = 1000; //老样子给个大数
    allocator&amp;lt;string&amp;gt; alloc; //拥有分配string内存能力的alloc
    auto const p = alloc.allocate(n); //分配n个未初始化的string
    auto q = p;
    //为string赋值
    alloc.construct(q++);           //*q为空
    alloc.construct(q++, 10, &#39;c&#39;);  //*q为cccccccccc
    alloc.construct(q++, &amp;quot;hi&amp;quot;);     //*q为 hi
    //cout &amp;lt;&amp;lt; *q-- &amp;lt;&amp;lt; endl;        //q所指向的内存还未构造，会发生未知的行为
    while (q != p) {
        alloc.destroy(--q);        //要挨个摧毁元素
    }
    alloc.deallocate(p, n);       //摧毁内存空间
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
">C++特性</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/z9c6qfcIW/"" data-c="
          &lt;h1 id=&#34;1-uboot的基础知识&#34;&gt;1. uboot的基础知识&lt;/h1&gt;
&lt;h2 id=&#34;11-uboot的cpu初始目录&#34;&gt;1.1 uboot的cpu初始目录&lt;/h2&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;1&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305251339147.png&#34; alt=&#34;初始目录文件&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h2 id=&#34;12-uboot编译的config文件&#34;&gt;1.2 uboot编译的config文件&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;新版uboot中的&lt;code&gt;config&lt;/code&gt;文件是在&lt;code&gt;./configs&lt;/code&gt;文件夹，后缀名为&lt;code&gt;xxx_defconfig&lt;/code&gt;（选择配置项菜单的文件），并且与&lt;code&gt;kconfig&lt;/code&gt;（是所有可以配置的菜单项的目录文件）配合，生成对应的&lt;code&gt;.config&lt;/code&gt;文件，在程序启动时加载&lt;code&gt;.config&lt;/code&gt;文件生成&lt;code&gt;config.h&lt;/code&gt;文件。&lt;/li&gt;
&lt;li&gt;2012版的uboot 在&lt;code&gt;./include/configs&lt;/code&gt;目录下直接有对应芯片的&lt;code&gt;xxx.h&lt;/code&gt;文件，&lt;strong&gt;&lt;code&gt;kconfig&lt;/code&gt;文件也不是配置文件，而是一个shell脚本&lt;/strong&gt;，通过对&lt;code&gt;make xxx&lt;/code&gt;输入参数做匹配，选择对应单板的config头文件，并链接编译。通过&lt;code&gt;make&lt;/code&gt; 按&lt;code&gt;tab&lt;/code&gt;键补全，会显示支持的配置命令，然后选择就可以了&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意：&lt;/strong&gt;&lt;code&gt;make s5p_goni&lt;/code&gt;会直接配置编译，&lt;code&gt;make s5p_goni_config&lt;/code&gt;只进行配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;uboot编译生成文件&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305271048963.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;2-uboot的源码分析&#34;&gt;2. uboot的源码分析&lt;/h1&gt;
&lt;p&gt;arm指令集取址流程说明&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;arm等高级CPU采用RISC精简指令集，这个指令集采用流水线方式（取值---&amp;gt;译码）来执行&lt;br&gt;
每条指令对应一个机器字长（32=4字节，64=8字节）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;汇编指令说明：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;b：简单的程序跳转，跳转到到目标标号处执行。&lt;br&gt;
bl：带链接程序跳转，也就是要带返回地址。在发生跳转前，将当前PC-4保存到R14中。也就是返回地址存在R14中，所以可以在子程序返回时只要MOV PC, LR即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;mrs：MRS    R1，CPSR   ; 将CPSR状态寄存器读取，保存到R1中&lt;br&gt;
msr：MRS    R2，SPSR    ; 将SPSR状态寄存器读取，保存到R2中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;bic：bic 是清零指令；用于清除操作数1的某些位，并把结果放置到目的寄存器中。 BIC{条件}{S}  目的寄存器，操作数1，操作数2&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bic	r0, r0, #0x1f&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;orr： orr 是位置1指令；用于在两个操作数上进行逻辑或运算，并把结果放置到目的寄存器中。ORR指令的格式为： ORR{条件}{S}  目的寄存器，操作数1，操作数2&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;orr	r0, r0, #0xd3&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;mrc：MRC{cond} p15,&amp;lt;Opcode_1&amp;gt;,&lt;Rd&gt;,&lt;CRn&gt;,&lt;CRm&gt;,&amp;lt;Opcode_2&amp;gt;&lt;br&gt;
mcr：MCR{cond} p15,&amp;lt;Opcode_1&amp;gt;,&lt;Rd&gt;,&lt;CRn&gt;,&lt;CRm&gt;,&amp;lt;Opcode_2&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cond：为指令执行的条件码。当cond忽略时指令为无条件执行。&lt;/li&gt;
&lt;li&gt;Opcode_1：协处理器的特定操作码. 对于CP15寄存器来说，opcode1=0&lt;/li&gt;
&lt;li&gt;Rd：源寄存器的ARM寄存器，其值将被传送到协处理器寄存器中，或将协处理器寄存器的值传送到该寄存器里面 ,通常为R0&lt;/li&gt;
&lt;li&gt;CRn：目标寄存器的协处理器寄存器，其编号是C~C15。&lt;/li&gt;
&lt;li&gt;CRm：协处理器中附加的目标寄存器或源操作数寄存器。如果不需要设置附加信息，将CRm设置为c0，否则结果未知&lt;/li&gt;
&lt;li&gt;Opcode_2：可选的协处理器特定操作码。（用来区分同一个编号的不同物理寄存器，当不需要提供附加信息时，指定为0）&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;21-uboot生成文件反汇编分析&#34;&gt;2.1 uboot生成文件反汇编分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;u-boot和u-boot.bin的区别
&lt;ul&gt;
&lt;li&gt;u-boot是含有调试信息的（用于测试开发）&lt;/li&gt;
&lt;li&gt;u-boot.bin纯二进制文件，不含任何调试信息（用于正式发版）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;objdump反汇编查看代码
&lt;blockquote&gt;
&lt;p&gt;arm-linux-objdump -S u-boot&lt;br&gt;
arm-linux-objdump -S u-boot | less    # 用管道重定向到less命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;编译器和汇编器区别
&lt;ul&gt;
&lt;li&gt;编译器：将C，C++等高级语言编译程为汇编语言&lt;/li&gt;
&lt;li&gt;汇编器：将汇编语言编译为二进制机器码&lt;br&gt;
&lt;strong&gt;异常中断处理&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;片内中断处理（三星为例）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;系统启动首先会读取ROM中的中断向量表，启动成功后，会创建一个自己的中断向量表，并将pc，loader等中断向量信息拷贝至自定义中断向量区域，后续的中断都会从自定义中断向量中触发。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305271442123.png&#34; alt=&#34;片内中断&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;片外中断处理（三星为例）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;不会再使用片内地址了，boot完后，片内地址被释放。arm在cp15协处理器中开辟了&lt;code&gt;VBAR&lt;/code&gt;的寄存器，只需要在自己片外启动程序中设置&lt;code&gt;VBAR&lt;/code&gt;的值为新的中断向量的地址，则后续中断请求就会到请求&lt;code&gt;VBAR&lt;/code&gt;地址的中断向量表进行中断处理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;启动源码分析&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;启动初始化源码：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;_pad:			.word 0x12345678 /* now 16*4=64 */
#endif	/* CONFIG_SPL_BUILD */

.global _end_vect
_end_vect:

	.balignl 16,0xdeadbeef          /*告诉编译器，下面.globl _TEXT_BASE指令执行时，要满足16字节对齐，否则填充0xdeadbeef至16字节再执行*/
/*************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don&#39;t start from memory!
 * setup Memory and board specific bits prior to relocation.
 * relocate armboot to ram
 * setup stack
 *
 *************************************************************************/

.globl _TEXT_BASE
_TEXT_BASE:
	.word	CONFIG_SYS_TEXT_BASE    /*源代码起始地址*/
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;中断请求初始化：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
	.word	0x0badc0de     /*初始系统没有加载完成，堆栈先用badcode填充*/

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
	.word 0x0badc0de
#endif

/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
	.word	0x0badc0de
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;reset处理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*
 * the actual reset code
 */

reset:
	bl	save_boot_params
	/*
	 * set the cpu to SVC32 mode
	 */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;22-entry等宏展开cpsr寄存器设置&#34;&gt;2.2 ENTRY等宏展开，CPSR寄存器设置&lt;/h2&gt;
&lt;h3 id=&#34;221-entryendproc宏的展开&#34;&gt;2.2.1 ENTRY，ENDPROC宏的展开&lt;/h3&gt;
&lt;p&gt;源代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *	__attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don&#39;t save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
	bx	lr			@ back to my caller
ENDPROC(save_boot_params)
	.weak	save_boot_params        //.weak：弱指令，当其他地方没有定义使用时才执行
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;宏定义在头文件里找，ENTRY宏的定义在&lt;code&gt;linux/linkage.h&lt;/code&gt;头文件中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ENTRY宏的展开代码：&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/************SYMBOL_NAME*************/
#define SYMBOL_NAME(X)		X
/*************LENTRY---ALIGN******************/
#ifndef __ALIGN
#define __ALIGN .align		4       //四字节对齐
#define ALIGN			__ALIGN
/*************LENTRY---SYMBOL_NAME_LABEL******************/
#ifdef __STDC__
#define SYMBOL_NAME_LABEL(X)	X##:
#else
#define SYMBOL_NAME_LABEL(X)	X:
#endif
/***************LENTRY**************/
#define LENTRY(name) \
	ALIGN; \
	SYMBOL_NAME_LABEL(name)
/***************ENTRY***************/
#define ENTRY(name) \
	.globl SYMBOL_NAME(name); \
	LENTRY(name)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ENDPROC宏的展开代码：&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*************ENDPROC---END***************/
#ifndef END
#define END(name) \
	.size name, .-name   //当前的标号name（ENDPROC） - 开始标号name（ENTRY）；即为程序大小
#endif
/************ENDPROC*************/
#ifndef ENDPROC
#define ENDPROC(name) \
	.type name STT_FUNC; \        //伪指令，告诉编译器数据类型为一个函数
	END(name)
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;222-cpsr寄存器设置&#34;&gt;2.2.2 CPSR寄存器设置&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;ARM处理器共有37个寄存器。这37个寄存器按其在用户编程中的功能划分，可分为2类寄存器，即31个通用寄存器和6个状态寄存器。这6个状态寄存器在ARM公司文件中其名称分别为：CPSR、SPSR_svc、SPSR_abt、SPSR_und、SPSR_irq和SPSR_fig。这12的作用分别如图1所示：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305271740756.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
所有处理器模式下都可访问当前程序状态寄存器CPSR。CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。在每种异常模式下都有一个对用的程序状态寄存器SPSR。当异常出现时，SPSR用于保存CPSR的状态，以便异常返回后恢复异常发生时的工作状态。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;条件码标志&lt;/strong&gt;&lt;br&gt;
N、Z、C、V，最高4位称为条件码标志。ARM的大多数指令可以条件执行的，即通过检测这些条件码标志来决定程序指令如何执行。&lt;br&gt;
各个条件码的含义如下：&lt;br&gt;
N：在结果是有符号的二进制补码情况下，如果结果为负数，则N=1；如果结果为非负数，则N=0。&lt;br&gt;
Z：如果结果为0，则Z=1;如果结果为非零，则Z=0。&lt;br&gt;
C：其设置分一下几种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于加法指令（包含比较指令CMN），如果产生进位，则C=1;否则C=0。&lt;/li&gt;
&lt;li&gt;对于减法指令（包括比较指令CMP），如果产生借位，则C=0;否则C=1。&lt;/li&gt;
&lt;li&gt;对于有移位操作的非法指令，C为移位操作中最后移出位的值。&lt;/li&gt;
&lt;li&gt;对于其他指令，C通常不变。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;V：对于加减法指令，在操作数和结果是有符号的整数时，如果发生溢出，则V=1;如果无溢出发生，则V=0;对于其他指令，V通常不发生变化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;控制位的作用在图1中可以看出&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;23-cp15协处理器基础&#34;&gt;2.3 CP15协处理器基础&lt;/h2&gt;
&lt;h3 id=&#34;231-vbar异常向量表基址映射&#34;&gt;2.3.1 VBAR异常向量表基址映射&lt;/h3&gt;
&lt;p&gt;源代码展示&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
#if !(defined(CONFIG_OMAP44XX) &amp;amp;&amp;amp; defined(CONFIG_SPL_BUILD))
	/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
	mrc	p15, 0, r0, c1, c0, 0	@ Read CP15 SCTRL Register
	bic	r0, #CR_V		@ V = 0  
	mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTRL Register

	/* Set vector address in CP15 VBAR register */
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bic r0,#CR_V：V=0为普通中断，跳转地址&lt;code&gt;0x00000000&lt;/code&gt;，V=1位高级中断（可以跳到自定义地址），默认地址&lt;code&gt;0xFFFF0000&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;232-cp15子过程分析cache操作&#34;&gt;2.3.2 CP15子过程分析——cache操作&lt;/h3&gt;
&lt;p&gt;源代码分析&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT     //如果为片内则执行，若为片外，则因为会先执行DDR初始化，所以此处便不会重发执行
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;icache操作源码分析&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*************************************************************************
 *
 * cpu_init_cp15
 *
 * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
 * CONFIG_SYS_ICACHE_OFF is defined.
 *
 *************************************************************************/
ENTRY(cpu_init_cp15)
	/*
	 * Invalidate L1 I/D
	 */
	mov	r0, #0			@ set up for MCR
	mcr	p15, 0, r0, c8, c7, 0	@ invalidate TLBs
	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache     //设置缓存失效
	mcr	p15, 0, r0, c7, c5, 6	@ invalidate BP array  //分支预测失效
	mcr     p15, 0, r0, c7, c10, 4	@ DSB       //DSB伪指令（推荐），直接使用指令，可能会导致多核CPU数据不同步
	mcr     p15, 0, r0, c7, c5, 4	@ ISB       //ISB伪指令（推荐，同上）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;缓存实现的方式有三种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;invalidate icache：设置指令的有效位为无效，则CPU会从RAM直接取数据&lt;/li&gt;
&lt;li&gt;flush：清空icache缓存中的所有数据，则CPU会从RAM中直接获取数据（速度比较慢）&lt;/li&gt;
&lt;li&gt;enable：使能用或不用这个icache缓存功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;233-cp15子过程分析分支预测mmu操作&#34;&gt;2.3.3 CP15子过程分析——分支预测，MMU操作&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;影响现代处理器性能的两大关键因素是cache和分支预测&lt;br&gt;
调用子函数：bl cpu_init_cp15&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;2331-分支预测&#34;&gt;2.3.3.1 分支预测&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;分支预测的目的&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在冯诺依曼的存储指令结构下，指令的执行包含有三种冒险：&lt;strong&gt;结构冒险&lt;/strong&gt;、&lt;strong&gt;数据冒险&lt;/strong&gt;和&lt;strong&gt;控制冒险&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;结构冒险&lt;/strong&gt;：是指硬件部件不足导致指令无法继续执行；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据冒险&lt;/strong&gt;：是指指令所需要的数据受到前面指令的影响，暂时无法取用，从而导致指令无法继续执行；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制冒险&lt;/strong&gt;：是指分支指令在控制程序的过程中中断指令流，从而导致程序无法继续执行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分支预测技术是指处理器在遇到分支指令时不再傻傻地等待分支结果，而是直接在取指阶段预测分支“跳”或者“不跳”以及跳转目标地址，目的是根据预测结果来实现不间断的指令流，从而让处理器的CPI再度接近理想情况中的1 .&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分支预测要预测两件事：&lt;strong&gt;分支指令的跳转方向&lt;/strong&gt;，&lt;strong&gt;分支指令的跳转目标地址&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;分支方向的预测&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;静态预测&lt;/li&gt;
&lt;li&gt;根据最后一次结果预测&lt;/li&gt;
&lt;li&gt;基于两位饱和计数预测&lt;/li&gt;
&lt;li&gt;基于局部历史的预测&lt;/li&gt;
&lt;li&gt;基于全局历史的预测&lt;/li&gt;
&lt;li&gt;竞争的分支预测&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;分支地址的预测&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接跳转类型的分支预测&lt;/li&gt;
&lt;li&gt;BTB缺失的处理&lt;/li&gt;
&lt;li&gt;间接跳转类型的分支预测&lt;/li&gt;
&lt;li&gt;其他预测方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;2332-mmu操作&#34;&gt;2.3.3.2 MMU操作&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;MMU是什么&lt;/strong&gt;&lt;br&gt;
MMU是Memory Management Unit的缩写，中文名是内存管理单元，它是&lt;strong&gt;中央处理器（CPU）中用来管理虚拟存储器&lt;/strong&gt;、物理存储器的控制线路，同时也负责虚拟地址映射为物理地址，以及提供硬件机制的内存访问授权，多用户多进程操作系统。&lt;br&gt;
其功能主要有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;完成虚拟地址到物理地址的转化&lt;/li&gt;
&lt;li&gt;对相应的地址空间的访问权限控制&lt;/li&gt;
&lt;li&gt;与操作系统的内存管理程序一起协作对内存进行管理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;为什么要关闭MMU呢？&lt;/strong&gt;&lt;br&gt;
mmu在设备上电之初是没有任何作用的，也就是说，在u-boot的初始化之初执行汇编的那一段代码中，&lt;br&gt;
包括后面的初始化一些具体的外设时，访问的都是实际的地址，mmu的打开起不到任何的意义，为了不影响启动之初对程序的启动，关闭掉mmu设备是常用的做法。&lt;/p&gt;
&lt;p&gt;代码说明：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*
	 * disable MMU stuff and caches
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002000	@ clear bits 13 (--V-)
	bic	r0, r0, #0x00000007	@ clear bits 2:0 (-CAM)
	orr	r0, r0, #0x00000002	@ set bit 1 (--A-) Align
	orr	r0, r0, #0x00000800	@ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
	bic	r0, r0, #0x00001000	@ clear bit 12 (I) I-cache
#else
	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-cache
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;24-cpu_initcrit子过程分析&#34;&gt;2.4 cpu_initcrit子过程分析&lt;/h2&gt;
&lt;h3 id=&#34;241-led显示启动运行状态&#34;&gt;2.4.1 LED显示启动运行状态&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;现在&lt;strong&gt;串口调试等方式还没有建立&lt;/strong&gt;，为了确信程序运行到这里了，可以在这里设计一个点亮LED的程序，在lowlevel_init中调用，这样就可以观察到程序运行到哪一步了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;初始化代码入口函数：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;    /* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl	cpu_init_cp15   //初始化cp15协处理器（关闭mmu，分支预测等）
    bl	cpu_init_crit    //初始化cp15后，就好执行此处初始化函数
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;跳转至对应的初始化函数（&lt;strong&gt;跨文件跳转&lt;/strong&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#ifndef CONFIG_SKIP_LOWLEVEL_INIT
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
ENTRY(cpu_init_crit)
    /*
    * Jump to board specific initialization...
    * The Mask ROM will have already initialized
    * basic memory. Go here to bump up clock rate and handle
    * wake up conditions.
    */
    b	lowlevel_init		@ go setup pll,mux,memory  //在lowlevel_init.S文件中
ENDPROC(cpu_init_crit)
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;GPIO点亮代码：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/*************跳转函数必须放在cpu_init_crit前面**************/
bl  gpio_out    //bl:带链跳转，初始化GPIO引脚
bl  led1_on     //点亮LED等代码

........
/****************GPIO点亮等******************/
gpio_out:
	ldr	r11, =0xE0200200
	ldr	r12, =0x00001111
	str	r12, [r11]
	
	ldr	r11, =0xE0200284
	ldr	r12, =0XF
	str	r12, [r11]
	mov	pc, lr
	
.globl led1_on
led1_on:
	ldr	r11, =0xE0200284
	ldr	r12, [r11]
	bic	r12, r12, #1       //清0r12的第一位，再回写r12中
	str r12, [r11]      //
	mov	pc, lr
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;242-gpio电路分析&#34;&gt;2.4.2 GPIO电路分析&lt;/h3&gt;
&lt;p&gt;推挽输出和开漏输出参考视频链接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;爱上半导体：&lt;a href=&#34;https://www.bilibili.com/video/BV1Pr4y1n74J/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;工科孙老师：&lt;a href=&#34;https://www.bilibili.com/video/BV1D84y1c7GV/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GPIO引脚电路：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;三种输出状态&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280922984.png&#34; alt=&#34;GPIO三种输出状态图&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;推挽输出
&lt;ul&gt;
&lt;li&gt;推：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280924938.png&#34; alt=&#34;GPIO电流推&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;挽：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280926357.png&#34; alt=&#34;GPIO电流挽&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;开漏输出&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280930343.png&#34; alt=&#34;开漏模式&#34; loading=&#34;lazy&#34;&gt;
&lt;ul&gt;
&lt;li&gt;5v控制3.3v&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280934562.png&#34; alt=&#34;开漏设置高电平&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280934073.png&#34; alt=&#34;开漏设置低电平&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;多个GPIO同时控制一个输入
&lt;ul&gt;
&lt;li&gt;推挽模式（多个GPIO控制同一个输入，一个mos管必定短路烧毁）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280937891.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;开漏模式&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280940969.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;总结&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305280942528.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;243-u-boot烧录镜像分析&#34;&gt;2.4.3 u-boot烧录镜像分析&lt;/h3&gt;
&lt;p&gt;参考视频链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1s4411t7eT?p=10&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;mkv210芯片uboot加载的片内（iROM）内存仅为16k，若uboot大小超过16k怎么办呢？&lt;br&gt;
通过代码限制，将uboot中重要的前16k内容（包括校验和）先拷贝进去，等完成内存（DDR）初始化后，将剩余的全部拷贝到内存中，进行系统启动，并清除片内内存（IROM）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;片内内存和片外内存的数据关系图示&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;校验和&lt;/strong&gt;：除校验和16字节外的其余内容累加求和&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305281623781.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mkv210_image.c源码（三星的S5PV210所特有的）&lt;br&gt;
作用：保证文件小于16k，并为BL1添加校验头。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;    /*
    * mkv210_image.c作用：将usb启动时使用的xxx_usb.bin制作得到由sd卡启动的镜像xxx_sd.bin
    * 本文件来自于友善之臂的裸机教程，据友善之臂的文档中讲述，本文件是一个热心网友提供，在此表示感谢。
    *
    * 在BL0阶段，Irom内固化的代码读取nandflash或SD卡前16K的内容，
    * 并比对前16字节中的校验和是否正确，正确则继续，错误则停止。
    */
    #include &amp;lt;stdio.h&amp;gt;
    #include &amp;lt;string.h&amp;gt;
    #include &amp;lt;stdlib.h&amp;gt;

    #define BUFSIZE                 (16*1024)
    #define IMG_SIZE                (16*1024)
    #define SPL_HEADER_SIZE         16
    #define SPL_HEADER            &amp;quot;S5PC110 HEADER  &amp;quot;    // 16字节，随便16个字节即可

    int main (int argc, char *argv[])
    {
        FILE *fp;
        char *Buf, *a;
        int BufLen;
        int nbytes, fileLen;
        unsigned int checksum, count;
        int i;

        // 1.检查参数个数是否为3
        if (argc != 3)
        {
            printf(&amp;quot;Usage: %s &amp;lt;source file&amp;gt; &amp;lt;destination file&amp;gt;\n&amp;quot;, argv[0]);
            return -1;
        }

        // 2.分配16K的buffer并请零
        BufLen = BUFSIZE;
        Buf = (char *)malloc(BufLen);
        if (!Buf)
        {
            printf(&amp;quot;malloc buffer failed!\n&amp;quot;);
            return -1;
        }
        memset(Buf, 0x00, BufLen);

        // 3.读源bin到buffer
        // 3.1 打开源bin
        fp = fopen(argv[1], &amp;quot;rb&amp;quot;);
        if( fp == NULL)
        {
            printf(&amp;quot;source file open error\n&amp;quot;);
            free(Buf);
            return -1;
        }
        // 3.2获取源bin长度
        fseek(fp, 0L, SEEK_END);                                // 定位到文件尾
        fileLen = ftell(fp);                                    // 得到文件长度
        fseek(fp, 0L, SEEK_SET);                                // 再次定位到文件头
        // 3.3源bin长度不得超过16K-16byte，
        // 注意若裸机程序大于16KB，则裸机程序会出问题，在后续做LCD实验时需特别注意
        count = (fileLen &amp;lt; (IMG_SIZE - SPL_HEADER_SIZE))
            ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
        // 3.4在buffer[0~15]中存放&amp;quot;S5PC110 HEADER  &amp;quot;
        memcpy(&amp;amp;Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
        // 3.5读源bin到buffer[16]
        nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);
        if ( nbytes != count )
        {
            printf(&amp;quot;source file read error\n&amp;quot;);
            free(Buf);
            fclose(fp);
            return -1;
        }
        fclose(fp);

        // 4.计算校验和
        // 4.1从第16byte开始计算，把buffer中所有的字节数据加和起来得到的结果
        a = Buf + SPL_HEADER_SIZE;
        for(i = 0, checksum = 0; i &amp;lt; IMG_SIZE - SPL_HEADER_SIZE; i++)
            checksum += (0x000000FF) &amp;amp; *a++;
        // 4.2将校验和保存在buffer[8~12]
        a = Buf + 8;    // Buf是xxx_sd.bin的起始地址，+8表示向后位移2个字，也就是说写入到第3个字
        *((unsigned int *)a) = checksum;

        // 5.拷贝buffer中的内容到目的bin
        // 5.1打开目的bin
        fp = fopen(argv[2], &amp;quot;wb&amp;quot;);
        if (fp == NULL)
        {
            printf(&amp;quot;destination file open error\n&amp;quot;);
            free(Buf);
            return -1;
        }
        // 5.2将16k的buffer拷贝到目的bin中
        a = Buf;
        nbytes = fwrite(a, 1, BufLen, fp);
        if (nbytes != BufLen)
        {
            printf(&amp;quot;destination file write error\n&amp;quot;);
            free(Buf);
            fclose(fp);
            return -1;
        }

        free(Buf);
        fclose(fp);

        return 0;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用hexdump分析16进制代码&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大端存储&lt;br&gt;
数据的高字节存储在低地址中，数据的低字节存储在高地址中 如：&lt;br&gt;
&lt;strong&gt;用于网络数据报协议传输,暂时不可替代。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;小端存储&lt;br&gt;
数据的低位放在低地址空间，数据的高位放在高地址空间&lt;br&gt;
简记：小端就是低位对应低地址，高位对应高地址&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;25-编写自己的boot程序&#34;&gt;2.5 编写自己的boot程序&lt;/h2&gt;
&lt;h3 id=&#34;251-gcc编译过程详解&#34;&gt;2.5.1 gcc编译过程详解&lt;/h3&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;2&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305282239296.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h3 id=&#34;252-文件链接过程&#34;&gt;2.5.2 文件链接过程&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;text：存放代码&lt;br&gt;
data：存放已经初始化且不为0的全局变量&lt;br&gt;
bss：存放初始化为0或未初始化的全局变量，以及标记说明&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;工具链使用&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;arm-none-eabi-as /arm-none-eabi-gcc -c        #编译&lt;/li&gt;
&lt;li&gt;arm-none-eabi-ld -T      #链接文件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;链接示意图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305282318742.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;代码示例&lt;br&gt;
&lt;code&gt;ldr fp, [pc,#20]&lt;/code&gt;: 中&lt;code&gt;[pc,#20]&lt;/code&gt;属于基址变址寻址，pc+20的地方取址&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mystart.S文件——uboot初始化代码&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/***********处理异常中断处理——根据cpu的中断向量长度来设置`b reset`的个数***********/
b reset
b reset
b reset
b reset
b reset
b reset
b reset
b reset
/**********初始化uboot的启动函数************/
reset:  
        bl gpio_out
        bl led2_on
        mov r0,r1          //无意义的空指令
        mov r1,r2
        mov r2,r3
        mov r3,r4
        mov r4,r5
1:
        b 1b    @ b:back jump;f:forward jump
/************配置GPIO输出功能************/
gpio_out:
        ldr     r11, =0xE0200200
        ldr     r12, =0x00001111
        str     r12, [r11]

        ldr     r11, =0xE0200284
        ldr     r12, =0XF
        str     r12, [r11]
        mov     pc, lr
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;mylowlevel.S文件——led点亮代码&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;.globl led2_on  @ .globl:marking the instruction can be used in other file
led2_on:
        ldr     r11, =0xE0200284
        ldr     r12, [r11]
        bic     r12, r12, #[1&amp;lt;&amp;lt;1]       @ left shift one step,turn on led2
        str     r12, [r11]
        mov     pc, lr              
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;myboot.lds链接配置文件&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;SECTIONS
{
        . = 0xD0020010; /** 16k memory start addr*/
        .text : {
                mystart.o
                * (.text)
        }
        .data : {
                * (.data)
        }
        .bss_start = .;
        .bss : {
                * (.bss)
        }
        .bss_end = .;
}                                    
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;objdump调试链接文件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arm-none-eabi-objdump -S myboot&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;objcopy剥离注释说明生成二进制文件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arm-none-eabi-objcopy -O binary myboot myboot.bin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;253-makefile命令&#34;&gt;2.5.3 Makefile命令&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;.PHONY&lt;br&gt;
但是如果Makefile当前目录下有一个和clean目标同名的文件clean，那么这么写就会有问题，因为clean文件永远是最新的，所以clean目标的命令永远得不到执行，为了解决这个问题，Makefile提供了伪目标这个功能，所以新的清除命令可以写成下面这样：&lt;pre&gt;&lt;code class=&#34;language-make&#34;&gt;.PHONY : clean
clean :
    $(RM) *.o target
&lt;/code&gt;&lt;/pre&gt;
只要有这个声明，不管是否有clean文件，make clean都会得到执行，make在执行此规则时不会去试图查找隐含规则来创建它，这样也提高了make的最新效率。&lt;/li&gt;
&lt;li&gt;自动化变量
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$@&lt;/code&gt;：所有目标文件集合&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$&amp;lt;&lt;/code&gt;：所有依赖文件中的第一个&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$^&lt;/code&gt;：所有依赖文件集合&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模式匹配
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%&lt;/code&gt;：%.o，%.s&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;26-uart分析&#34;&gt;2.6 UART分析&lt;/h2&gt;
&lt;h3 id=&#34;261-异步通信uart和同步通信spi概念&#34;&gt;2.6.1 异步通信（UART）和同步通信（SPI）概念&lt;/h3&gt;
&lt;p&gt;通信协议（SPI，UART，I2C）：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/450963372&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同步串行通信（SPI）&lt;br&gt;
在SPI设备中，设备分为主机与从机系统。主机是控制设备（通常是微控制器），而从机（通常是传感器，显示器或存储芯片）从主机那获取指令。一套SPI通讯共包含四种信号线：MOSI (Master Output/Slave Input) – 信号线，主机输出，从机输入。MISO (Master Input/Slave Output) – 信号线，主机输入，从机输出。SCLK (Clock) – 时钟信号。SS/CS (Slave Select/Chip Select) – 片选信号。
&lt;ul&gt;
&lt;li&gt;通信方式：全双工通信&lt;/li&gt;
&lt;li&gt;缺点：通讯距离短，一般适用于开发板直接连线通信；SPI使用四根线（I2C和UART使用两根线），没有信号接收成功的确认（I2C拥有此功能），没有任何形式的错误检查（如UART中的奇偶校验位等）。&lt;/li&gt;
&lt;li&gt;优点：可以无中断传输数据，可以连续地发送或接收任意数量的位；没有像I2C这样的复杂的从站寻址系统，数据传输速率比I2C更高（几乎快两倍）。独立的MISO和MOSI线路，可以同时发送和接收数据。&lt;/li&gt;
&lt;li&gt;图示：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291552101.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;异步串行通信（UART）&lt;br&gt;
UART代表通用异步接收器/发送器也称为串口通讯，它不像SPI和I2C这样的通信协议，而是微控制器中的物理电路或独立的IC。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291558790.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291624175.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
UART的主要目的是发送和接收串行数据，其最好的优点是它仅使用两条线在设备之间传输数据。 UART属于&lt;strong&gt;异步通讯&lt;/strong&gt;，这意味着&lt;strong&gt;没有时钟信号&lt;/strong&gt;，取而代之的是在数据包中添加开始和停止位。
&lt;ul&gt;
&lt;li&gt;数据报文格式：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291605763.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;起始位&lt;/strong&gt;：发送1位逻辑0（低电平），开始传输数据。&lt;br&gt;
&lt;strong&gt;数据位&lt;/strong&gt;：可以是5~8位的数据，先发低位，再发高位，一般常见的就是8位（1个字节），其他的如7位的ASCII码。&lt;br&gt;
&lt;strong&gt;校验位&lt;/strong&gt;：奇偶校验，将数据位加上校验位，1的位数为偶数（偶校验），1的位数4为奇数（奇校验）。&lt;br&gt;
&lt;strong&gt;停止位&lt;/strong&gt;：停止位是数据传输结束的标志，可以是1/1.5/2位的逻辑1（高电平）。&lt;br&gt;
&lt;strong&gt;空闲位&lt;/strong&gt;：空闲时数据线为高电平状态，代表无数据传输&lt;/li&gt;
&lt;li&gt;通信方式：全双工通信（需要协调波特率）&lt;/li&gt;
&lt;li&gt;缺点：
&lt;ul&gt;
&lt;li&gt;数据帧的大小最大为9位&lt;/li&gt;
&lt;li&gt;不支持多个从属系统或多个主系统&lt;/li&gt;
&lt;li&gt;每个UART的波特率必须在彼此的10％之内&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优点：
&lt;ul&gt;
&lt;li&gt;仅使用两根电线&lt;/li&gt;
&lt;li&gt;无需时钟信号&lt;/li&gt;
&lt;li&gt;具有奇偶校验位以允许进行错误检查&lt;/li&gt;
&lt;li&gt;只要双方都设置好数据包的结构&lt;/li&gt;
&lt;li&gt;有据可查并得到广泛使用的方法&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;图示：&lt;br&gt;
启动条件：当SCL是高电平时，SDA从高电平向低电平切换。停止条件：当SCL是高电平时，SDA由低电平向高电平切换。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291602981.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;波特率的概念&lt;br&gt;
波特率，即调制速率，模拟线路信号的速率，以波形每秒的振荡数来衡量。如果数据不压缩，波特率等于每秒钟传输的数据位数，单位bit/s或bps。注意和比特率不严格相关&lt;br&gt;
在UART中：BYTE/s = bps / 10
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;波特率与传输举例的关系&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;data rate（bps）&lt;/th&gt;
&lt;th&gt;maximum（meters）&lt;/th&gt;
&lt;th&gt;distance（feet）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;19200&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9600&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4800&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2400&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;304&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;608&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;1216&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;262-uart时钟频率配置&#34;&gt;2.6.2 UART时钟频率配置&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;波特率设置（UBRDIV0，R/W，Address=0x----_----)&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291655458.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;信道划分槽（UDIVSLOT0，R/W，Address=0x----_----）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291701675.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;串口数据发送寄存器设置（UTXH0，W，Address=0x----_----)&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291708478.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;100:
    ...........
    ldr r0,=0xE2900020
    ldr r1,=0x55            @ UTH0 = &#39;U&#39;
    str r1,[r0]                 @ 将r1中的内存传输到r0地址缓冲区中

    ldr r1,=0x61            @UTH0 = &#39;a&#39;
    str r1,[r0]                 @ 将r1中的内存传输到r0地址缓冲区中
    ...........
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;str指令&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;str r0，[r1]，＃8             ；将R0中的字数据写入以r1为地址的存储器中，并将新地址R1＋8写入R1。&lt;br&gt;
str r0，[r1，＃8]             ；将r0中的字数据写入以r1＋8为地址的存储器中。&lt;br&gt;
str     r1, [r0]                       ；将r1寄存器的值，传送到地址值为r0的（存储器）内存中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;串口数据接受寄存器设置（URXH0，W，Address=0x----_----）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291710979.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;263-可能存在问题&#34;&gt;2.6.3 可能存在问题&lt;/h3&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://www.bilibili.com/video/BV1s4411t7eT/?p=25&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;编译文件顺序&lt;/strong&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305292320659.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;问题描述&lt;/strong&gt;：&lt;br&gt;
自定义的lowlevel_init.S文件编译时处于编译的最末尾，在后续截取16k有效uboot时，会被截取掉&lt;br&gt;
&lt;strong&gt;解决方法&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;vim makefile
# 搜索变量 “LIBS”
# 将$(LIBBOARD)变量放在前面即可
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;start.S为最重要的（必须有的），其他的都是可有可无的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;264-uart显示内存中数据&#34;&gt;2.6.4 UART显示内存中数据&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;原理图展示：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;代码展示（lowlevel_init.S)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示代码&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;display_hex:
    ldr r1,=0xE2900020

    ldr r2,=0x30
    str r2,[r1]            @UTH0=&#39;0&#39;

    ldr r2,=0x78
    str r2,[r1]            @UTH0=&#39;x&#39;

    ldr r3,=28           @ 记录右移位数

disphex_loop_cnt:
    lsr r2, r0, r3          @将r0的数据右移28位，存入r2
    and r2, r2, #0xF    @ 清除最后一位外的其他位,并存入r2
    cmp r2,#10
    addmi   r2,r2,#0x30     @cmp小于10则累加
    addpl   r2,r2,#0x37     @cmp大于10则累加
    str r2,[r1]

    sub r3,r3,#4
    cmp r3,#0
    bpl disphex_loop_cnt    @若没移位完，继续循环

    ldr r2,=0xA
    str r2,[r1]     @UTH0=&#39;\r&#39;

    ldr r2,=0x0
    str r2,[r1]     @UTH0=&#39;\n&#39;

    mov     pc,lr       @通过bl跳转，故需要将lr1放入pc指针，进行返回
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;uart初始化代码&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;    /* for UART */
    bl	uart_asm_init

    ldr	r0, =0x1234abcd         @内存中要读入的数据
    bl	display_hex                 @跳转的处理函数

    bl	internal_ram_init
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;27-ddr内存&#34;&gt;2.7 DDR内存&lt;/h2&gt;
&lt;h3 id=&#34;271-sram-dram-ddr-sdram存储器的区别&#34;&gt;2.7.1 SRAM、DRAM、DDR、SDRAM存储器的区别&lt;/h3&gt;
&lt;p&gt;内存可分为DRAM动态随机存取内存和SRAM静态随机存取内存两种。两种存储器都是挥发性的内存，SRAM的主要使用flip-flop正反器，通常用于快取(Cache)，而DRAM则是使用电容器及晶体管组成。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305301025932.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SRAM&lt;br&gt;
SRAM是一种基于触发器的存储器，使用稳定的存储电路来存储和保持数据。每个存储单元由一个存储器单元和控制电路组成，其中&lt;strong&gt;存储器单元由多个触发器构成&lt;/strong&gt;，能够存储比特数据。由于采用了触发器结构，SRAM在不断刷新的过程中保持数据的&lt;strong&gt;稳定性&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;特点：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;由于SRAM的存储单元采用稳定的触发器结构，不需要进行定期刷新操作。数据可以一直保持稳定，无需周期性刷新。&lt;br&gt;
SRAM的访问速度非常快，因为数据存储在触发器中，可以立即读取和写入。SRAM具有较低的访问延迟和高速的读写性能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;用途：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;高速缓存&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;DRAM&lt;br&gt;
DRAM是一种基于电容的存储器，使用&lt;strong&gt;电容来存储和表示数据&lt;/strong&gt;。每个存储单元由一个电容和一个访问晶体管组成。电容在存储器中充电或放电来表示数据的0和1。由于电容会逐渐漏电，DRAM需要定期刷新以保持数据的&lt;strong&gt;正确性&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;特点：&lt;/li&gt;
&lt;li&gt;由于DRAM的电容逐渐漏电，数据需要定期刷新以保持其正确性。DRAM需要通过刷新操作周期性地重新写入数据，否则数据会丢失。&lt;/li&gt;
&lt;li&gt;由于DRAM的电容逐渐漏电，数据需要定期刷新以保持其正确性。DRAM需要通过刷新操作周期性地重新写入数据，否则数据会丢失。&lt;/li&gt;
&lt;li&gt;用途：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;内存条&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;DDR&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;DDR其实指的是DDRSDRAM&lt;br&gt;
DDR2 是 2 * 2 * SDRAM&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;SDRAM&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;同步动态随机存储器&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;272-ddr在cpu中的架构&#34;&gt;2.7.2 DDR在CPU中的架构&lt;/h3&gt;
&lt;p&gt;CPU中DRAM的架构图&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305301338753.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
DRAM内部实现结构&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305301340730.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;273-ddr内存初始化&#34;&gt;2.7.3 DDR内存初始化&lt;/h3&gt;
&lt;h3 id=&#34;274-随机存储和顺序存储方式&#34;&gt;2.7.4 随机存储和顺序存储方式&lt;/h3&gt;
&lt;p&gt;随机存储：内存，&lt;br&gt;
顺序存储：机械硬盘，磁带&lt;/p&gt;
&lt;h2 id=&#34;28-board_init_f函数分析&#34;&gt;2.8 board_init_f函数分析&lt;/h2&gt;
&lt;p&gt;board_init_f：调用c语言函数&lt;/p&gt;
&lt;h3 id=&#34;281-堆栈的初始化&#34;&gt;2.8.1 堆栈的初始化&lt;/h3&gt;
&lt;p&gt;代码示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)                  @初始化堆栈指针
	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */   
	ldr	r0,=0x00000000      @设置函数入口参数
	bl	board_init_f            @跳转c语言函数，位于：u-boot-2012.10\arch\arm\lib\board.c

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;282-代码分析&#34;&gt;2.8.2 代码分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;board_init_f代码分析&lt;/li&gt;
&lt;li&gt;&lt;code&gt;u-boot-2012.10\arch\arm\include\asm\global_data.h&lt;/code&gt;代码分析&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm (&amp;quot;r8&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;register&lt;/code&gt;：声明数据存储在寄存器中&lt;br&gt;
&lt;code&gt;volatile&lt;/code&gt;：声明变量不需要进行优化操作&lt;br&gt;
&lt;code&gt;asm (&amp;quot;r8&amp;quot;)&lt;/code&gt;：设置存储的寄存器&lt;/p&gt;
&lt;h2 id=&#34;29-mmc_relocate分析&#34;&gt;2.9 mmc_relocate分析&lt;/h2&gt;
&lt;h2 id=&#34;210-u-boot的镜像结构&#34;&gt;2.10 u-boot的镜像结构&lt;/h2&gt;
&lt;h2 id=&#34;211-&#34;&gt;2.11 -----&lt;/h2&gt;
&lt;h2 id=&#34;212-bootm启动流程分析&#34;&gt;2.12 bootm启动流程分析&lt;/h2&gt;
&lt;h2 id=&#34;213-附加芯片基础知识&#34;&gt;2.13 附加——芯片基础知识&lt;/h2&gt;
&lt;h3 id=&#34;2131-lowlevel_inits分析&#34;&gt;2.13.1 lowlevel_init.S分析&lt;/h3&gt;
&lt;p&gt;参考链接：&lt;a href=&#34;https://blog.csdn.net/a1598025967/article/details/106893165&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目录：u-boot-2012.10/board/samsung/goni/lowlevel_init.S&lt;br&gt;
u-boot-2012.10/arch/arm/cpu/armv7/lowlevel_init.S：弱属性，当没有任何芯片定义对应的&lt;code&gt;lowlevel.S&lt;/code&gt;文件时会执行&lt;br&gt;
u-boot-2012.10/board/samsung/goni/lowlevel_init.S：具体实现不同芯片的初始化代码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;#include头文件引入符与文件路径不同？&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305291022318.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;EVT1和EVT0？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EVT(工程验证测试)→DVT(设计验证测试)→MVT(生产验证测试)→PP(中试生产)→MP(导入量产)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;隐藏寄存器&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;skip_check_didle:
    ...................
    /*
    * Diable ABB block to reduce sleep current at low temperature
    * Note that it&#39;s hidden register setup don&#39;t modify it
    */
    ldr	r0, =0xE010C300
    ldr	r1, =0x00800000
    str	r1, [r0]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;IO设置
&lt;ul&gt;
&lt;li&gt;初始化ro文件&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;100:
    /* IO retension release */
    ldreq	r0, =S5PC100_OTHERS			@ 0xE0108200            
    ldrne	r0, =S5PC110_OTHERS			@ 0xE010E000
    .......................
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;S5PC100_OTHERS和S5PC110_OTHERS：定义在&lt;code&gt;u-boot-2012.10/arch/arm/include/asm/arch-s5pc1xx/power.h&lt;/code&gt;头文件中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;设置看门狗（watch timer）程序&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;100:
    ..................
    /* Disable Watchdog */
    ldreq	r0, =S5PC100_WATCHDOG_BASE		@ 0xEA200000
    ldrne	r0, =S5PC110_WATCHDOG_BASE		@ 0xE2700000
    str	r5, [r0]
    ...................
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;设置SRAM&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;RAM可分为&lt;strong&gt;静态存储器&lt;/strong&gt;（Static Random Access Memory,SRAM）和&lt;strong&gt;动态存储器&lt;/strong&gt;（Dynamic Random Access Memory）。SRAM中的存储单元相当于一个锁存器，只有0，1两个稳态；DRAM则是利用电容存储电荷来保存0和1两种状态，因此需要定时对其进行刷新，否则随着时间的推移，电容其中存储的电荷将逐渐消失。&lt;br&gt;
&lt;strong&gt;SRAM&lt;/strong&gt;：读写速度快，生产成本高，多用于容量较小的高速缓冲存储器。&lt;br&gt;
&lt;strong&gt;DRAM&lt;/strong&gt;：读写速度较慢，集成度高，生产成本低，多用于容量较大的主存储器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;100:
    .................
	/* setting SRAM */
	ldreq	r0, =S5PC100_SROMC_BASE
	ldrne	r0, =S5PC110_SROMC_BASE
	ldr	r1, =0x9
	str	r1, [r0]
    ...................
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;设置中断IRQ和FRQ等中断&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UART初始化代码分析&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
">Uboot源码分析</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/7nr0vBgCD/"" data-c="
          &lt;h1 id=&#34;1-配置u-boot的过程分析&#34;&gt;1. 配置u-boot的过程分析&lt;/h1&gt;
&lt;p&gt;uboot下载地址：&lt;a href=&#34;https://ftp.denx.de/pub/u-boot/&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
uboot参考文档：&lt;a href=&#34;https://elinux.org/index.php?title=U-Boot&amp;amp;action=edit&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
查看linux目录：tree . -d &amp;gt; uboot-arch.txt 命令&lt;/p&gt;
&lt;h2 id=&#34;11-uboot源码目录结构&#34;&gt;1.1 uboot源码目录结构&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt; ├── api
 ├── arch
 │   ├── arm                     //架构相关
 │   │   ├── cpu
 │   │   │   ├── armv7
 │   │   │   │   ├── mx6
 │   │   ├── dts
 │   │   │   └── include
 │   │   │       └── dt-bindings -&amp;gt; ../../../../include/dt-bindings
 │   │   ├── imx-common
 │   │   ├── lib
 │   │   ├── mach-rockchip
 │   │   │   ├── rk3036
 │   │   │   ├── rk3288
 │   │   │   └── rk3399
 │       └── lib
 ├── board                       //单板相关
 │   ├── freescale
 │   │   ├── common
 │   │   │   └── p_corenet
 │   │   ├── corenet_ds
 │   │   ├── mx6ul_14x14_ddr3_arm2
 │   │   ├── mx6ul_14x14_evk
 │   │   ├── mx6ul_14x14_lpddr2_arm2
 │   │   ├── mx6ull_ddr3_arm2
 │   │   ├── mx6ullevk
 ├── cmd             //通用命令
 │   ├── fastboot
 │   └── mvebu
 ├── common      //通用的
 │   ├── eeprom
 │   ├── init
 │   └── spl
 ├── configs
 ├── disk
 ├── doc             //参考文档
 ├── drivers         //各类驱动
 ├── dts
 ├── examples
 │   ├── api
 │   └── standalone
 ├── fs              //文件系统
 │   ├── cbfs
 │   ├── cramfs
 │   ├── ext4
 │   ├── fat
 │   ├── jffs2
 │   ├── reiserfs
 │   ├── sandbox
 │   ├── ubifs
 │   ├── yaffs2
 │   └── zfs
 ├── include
 ├── lib                 //库
 ├── Licenses
 ├── net              //网络协议（不是网卡驱动）
 ├── post
 ├── scripts
 ├── test
 └── tools
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;12-makefile技巧与分析&#34;&gt;1.2 Makefile技巧与分析&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;包含其他同级的Makefile文件（可以直接使用）：include a_Makefile
&lt;ul&gt;
&lt;li&gt;include a_Makefile：强制包含，没有报错&lt;/li&gt;
&lt;li&gt;-include c_Makefile：表示有就包含，没有就不管&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;打印Makefile的规则和变量：make -p&lt;/li&gt;
&lt;li&gt;把make命令规则和变量存入文件：make -p &amp;gt; 1.txt&lt;/li&gt;
&lt;li&gt;执行&lt;code&gt;vi 1.txt&lt;/code&gt;，使用vi命令删除注释：&lt;code&gt;:g/^# /d&lt;/code&gt;   (^：表示首行)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;13-uboot默认配置&#34;&gt;1.3. uboot默认配置&lt;/h2&gt;
&lt;h3 id=&#34;131-默认配置的过程&#34;&gt;1.3.1 默认配置的过程&lt;/h3&gt;
&lt;p&gt;IMX6ULL：&lt;code&gt;make mx6ull_14x14_evk_defconfig&lt;/code&gt;&lt;br&gt;
STM32MP157：&lt;code&gt;make stm32mp15_trusted_defconfig&lt;/code&gt;&lt;br&gt;
执行过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;制作工具：scripts/kconfig/conf&lt;/li&gt;
&lt;li&gt;把默认配置信息写入&amp;quot;.config&amp;quot;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305231337451.png&#34; alt=&#34;deconfig文件配置流程&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;分析过程&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;mx6ull_14x14_evk_defconfig: scripts/kconfig/conf
        $(Q)$&amp;lt; $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(kconfig)     
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;就是&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;UBOOTVERSION=2017.03   #设置uboot版本号
scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_evk_defconfig kconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;132-conf命令概述&#34;&gt;1.3.2 conf命令概述&lt;/h3&gt;
&lt;p&gt;分析scripts/kconfig/conf.c&lt;br&gt;
&lt;code&gt;make mx6ull_14x14_evk_defconfig V=1&lt;/code&gt;  #打印更多详细信息&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305232254233.png&#34; alt=&#34;make执行的详细信息&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;源码分析&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;defconfig_file = &amp;quot;arch/../configs/mx6ull_14x14_evk_defconfig&amp;quot;;
name = &amp;quot;Kconfig&amp;quot;

conf_parse(name);   //解析uboot根目录下的kconfig文件

conf_read(defconfig_file);      //读取配置文件

conf_set_all_new_symbols(def_default);     //设置new_symbols为默认值

conf_write(NULL);   //写到.config文件
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Kconfig: 这是一个通用文件，里面规定了一些依赖，比如：
&lt;ul&gt;
&lt;li&gt;如果是ARM架构，就默认选择A，B，C配置&lt;/li&gt;
&lt;li&gt;如果是RISC-V架构，就默认选择a，b，c配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;defconfig_file：这是厂家提供的，里面定义了
&lt;ul&gt;
&lt;li&gt;ARM架构&lt;/li&gt;
&lt;li&gt;自己的一些配置项&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;怎么处理呢？
&lt;ul&gt;
&lt;li&gt;使用defconfig_file的内容去解析Kconfig，把各个依赖的配置项也写入.config&lt;/li&gt;
&lt;li&gt;其他未涉及的配置项，给他们指定默认值&lt;/li&gt;
&lt;li&gt;写入.config&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;133-kconfdefconfig和config文件关系图&#34;&gt;1.3.3 kconf，defconfig和.config文件关系图&lt;/h3&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;1&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305242125156.png&#34; alt=&#34;kconf，defconfig和.config文件关系图&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;.config：&lt;br&gt;
①用来控制哪些目录/文件被编译&lt;br&gt;
②会生成一个头文件，标记哪些.h文件会被编译到.c文件中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;2-uboot-编译&#34;&gt;2. uboot 编译&lt;/h1&gt;
&lt;h2 id=&#34;21-uboot编译过程&#34;&gt;2.1 uboot编译过程&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;IMX6ULL:
# 配置单板开发的编译工具链
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 删除之前编译的所有依赖
make distclean
# 配置单板编译的内容
make mx6ull_14x14_evk_defconfig
# 编译
make
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;make过程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查、更新头文件，如：include/config.h、u-boot.cfg&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305242203075.png&#34; alt=&#34;make命令执行输出&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;制作工具&lt;/li&gt;
&lt;li&gt;交叉编译
&lt;ul&gt;
&lt;li&gt;编译哪些目录，文件？&lt;/li&gt;
&lt;li&gt;.c文件可能需要使用.config的配置值，它可以引用config.h&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;22-配置过程详细分析&#34;&gt;2.2 配置过程详细分析&lt;/h2&gt;
&lt;p&gt;uboot编译参考资料：&lt;a href=&#34;https://www.cnblogs.com/jianhua1992/archive/2022/11/02/16852774.html&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
kconfig语法参考资料：&lt;a href=&#34;https://www.rt-thread.org/document/site/#/development-tools/build-config-system/Kconfig&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Makefile的remake&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;all:
    @echo &amp;quot;hello world&amp;quot;
    @echo A=${A}
-include test
test:
    @echo create test file
    @echo &amp;quot;A=1&amp;quot; &amp;gt; test
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;先执行&lt;code&gt;-include test&lt;/code&gt;并执行关联的test文件，然后再进行&lt;code&gt;make&lt;/code&gt;编译整个文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;221-makefile的精简代码&#34;&gt;2.2.1 Makefile的精简代码&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;PHONY := _all
_all:

_all: all

KCONFIG_CONFIG	?= .config

# 定义一些常用变量
# 比如
# build := -f $(srctree)/scripts/Makefile.build obj
include scripts/Kbuild.include

# 定义交叉编译工具链
CC		= $(CROSS_COMPILE)gcc

-include include/config/auto.conf
-include include/config/auto.conf.cmd
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;

include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
		{ rm -f include/config/auto.conf; false; }
	$(Q)touch include/config/auto.conf

-include include/autoconf.mk
-include include/autoconf.mk.dep

# 里面又包含架构相关、CPU相关的文件
# sinclude $(srctree)/arch/$(ARCH)/config.mk      # include architecture dependend rules
# sinclude $(srctree)/$(CPUDIR)/config.mk         # include  CPU  specific rules
include config.mk

# 架构相关参数、相关文件、库
# head-y := arch/arm/cpu/$(CPU)/start.o
# libs-y += arch/arm/cpu/$(CPU)/
include arch/$(ARCH)/Makefile  

# 确定链接脚本
ifndef LDSCRIPT
	ifeq ($(wildcard $(LDSCRIPT)),)
		LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
	endif
	ifeq ($(wildcard $(LDSCRIPT)),)
		LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
	endif
	ifeq ($(wildcard $(LDSCRIPT)),)
		LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds
	endif
endif

# 默认要处理的库的路径
libs-y += lib/
libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
libs-$(CONFIG_OF_EMBED) += dts/
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/

# 取决于配置的库的路径
libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/
libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
libs-$(CONFIG_SYS_FSL_MMDC) += drivers/ddr/fsl/
libs-$(CONFIG_ALTERA_SDRAM) += drivers/ddr/altera/

u-boot-init := $(head-y)
u-boot-main := $(libs-y)

u-boot-dirs	:= $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples

$(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;

$(u-boot-dirs): prepare scripts
	$(Q)$(MAKE) $(build)=$@

# all目标要生成的依赖
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check

all:		$(ALL-y)

u-boot.hex u-boot.srec: u-boot FORCE
        $(call if_changed,objcopy)

u-boot:	$(u-boot-init) $(u-boot-main) u-boot.lds FORCE
	$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
	$(call cmd,smap)
	$(call cmd,u-boot__) common/system_map.o
endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Makefile：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;ifeq ($(KBUILD_SRC),)


# That&#39;s our default target when none is given on the command line
PHONY := _all
_all:

endif # ifeq ($(KBUILD_SRC),)

# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)

PHONY += all
_all: all

HOSTCC       = cc

# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.

KBUILD_MODULES :=
KBUILD_BUILTIN := 1

# 引入很多变量, 
# 比如:
# build := -f $(srctree)/scripts/Makefile.build obj
include scripts/Kbuild.include

# 定义交叉编译工具链
AS		= $(CROSS_COMPILE)as
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E


version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h

no-dot-config-targets := clean clobber mrproper distclean \
			 help %docs check% coccicheck \
			 ubootversion backup tests

config-targets := 0
mixed-targets  := 0
dot-config     := 1


ifeq ($(mixed-targets),1)
else
ifeq ($(config-targets),1)
else
PHONY += scripts
scripts: scripts_basic include/config/auto.conf
	$(Q)$(MAKE) $(build)=$(@)

ifeq ($(dot-config),1)
-include include/config/auto.conf
-include include/config/auto.conf.cmd
# To avoid any implicit rule to kick in, define an empty command
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;

# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
	@# If the following part fails, include/config/auto.conf should be
	@# deleted so &amp;quot;make silentoldconfig&amp;quot; will be re-run on the next build.
	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
		{ rm -f include/config/auto.conf; false; }
	@# include/config.h has been updated after &amp;quot;make silentoldconfig&amp;quot;.
	@# We need to touch include/config/auto.conf so it gets newer
	@# than include/config.h.
	@# Otherwise, &#39;make silentoldconfig&#39; would be invoked twice.
	$(Q)touch include/config/auto.conf

-include include/autoconf.mk
-include include/autoconf.mk.dep

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;222-配置文件进一步处理&#34;&gt;2.2.2 配置文件进一步处理&lt;/h3&gt;
&lt;p&gt;前面生成了.config，但是它不是最终版本的配置文件。&lt;/p&gt;
&lt;p&gt;顶层Makefile会包含2个配置文件：include/config/auto.conf、include/autoconf.mk。&lt;/p&gt;
&lt;p&gt;u-boot中有非常多的配置文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;.config：来自单板的默认配置、Kconfig&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;include/config/auto.conf：来自.config，去掉了很多注释&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;u-boot.cfg：它的内容跟头文件类似，来自&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;.config&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;头文件include/common.h，又包含了&amp;quot;#include &amp;lt;config.h&amp;gt;&amp;quot;，config.h内容如下:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;/* Automatically generated - do not edit */
#define CONFIG_IMX_CONFIG       board/freescale/mx6ullevk/imximage.cfg
#define CONFIG_BOARDDIR board/freescale/mx6ullevk
#include &amp;lt;config_defaults.h&amp;gt;
#include &amp;lt;config_uncmd_spl.h&amp;gt;
#include &amp;lt;configs/mx6ullevk.h&amp;gt;
#include &amp;lt;asm/config.h&amp;gt;
#include &amp;lt;linux/kconfig.h&amp;gt;
#include &amp;lt;config_fallbacks.h&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;include/autoconf.mk：来自u-boot.cfg，但是移除include/config/auto.conf的内容以免重复&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要进一步处理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
		{ rm -f include/config/auto.conf; false; }
	$(Q)touch include/config/auto.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述规则的命令里，有2个操作：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第1个操作相当于：make silentoldconfig，生成了文件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;include/config/auto.conf：来自.config，去掉了很多注释&lt;/li&gt;
&lt;li&gt;include/config/auto.conf.cmd：auto.conf的依赖规则&lt;/li&gt;
&lt;li&gt;include/generated/autoconf.h：内容跟auto.conf类似，是C语言的头文件&lt;/li&gt;
&lt;li&gt;include/config/tristate.conf：空文件&lt;/li&gt;
&lt;li&gt;include/generated/autoksyms.h：空文件&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;make silentoldconfig

%config: scripts_basic outputmakefile FORCE
        $(Q)$(MAKE) $(build)=scripts/kconfig $@

进入scripts/kconfig是它的Makefile: 
silentoldconfig: $(obj)/conf
        $(Q)mkdir -p include/config include/generated
        $(Q)test -e include/generated/autoksyms.h || \
            touch   include/generated/autoksyms.h
        $&amp;lt; $(silent) --$@ $(Kconfig)

scripts/kconfig/conf分析：
scripts/kconfig/conf  --silentoldconfig Kconfig
1. sync_kconfig = 1
2. name = &amp;quot;Kconfig&amp;quot;, conf_parse(name);
3. name = conf_get_configname(); = &amp;quot;.config&amp;quot;
4. conf_read(NULL);  # 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第2个操作相当于&lt;code&gt;make -f scripts/Makefile.autoconf&lt;/code&gt;，会生成一系列头文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ln -fsn arch-mx6 arch/arm/include/asm/arch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;include/autoconf.mk, {spl,tpl}/include/autoconf.mk&lt;/li&gt;
&lt;li&gt;include/config.h&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;scripts/Makefile.autoconf&lt;/code&gt;分析：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;__all: include/autoconf.mk include/autoconf.mk.dep

include include/config/auto.conf
include scripts/Kbuild.include

CC              = $(CROSS_COMPILE)gcc
CPP             = $(CC) -E

include config.mk

include/autoconf.mk.dep: include/config.h FORCE
        $(call cmd,autoconf_dep)

u-boot.cfg: include/config.h FORCE
        $(call cmd,u_boot_cfg)

include/autoconf.mk: u-boot.cfg
        $(call cmd,autoconf)

include/config.h: scripts/Makefile.autoconf create_symlink FORCE
        $(call filechk,config_h)

create_symlink:
ifdef CONFIG_CREATE_ARCH_SYMLINK
ifneq ($(KBUILD_SRC),)
        $(Q)mkdir -p include/asm
        $(Q)if [ -d $(KBUILD_SRC)/arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \
                dest=arch/$(ARCH)/mach-$(SOC)/include/mach;                     \
        else                                                                    \
                dest=arch/$(ARCH)/include/asm/arch-$(if $(SOC),$(SOC),$(CPU));  \
        fi;                                                                     \
        ln -fsn $(KBUILD_SRC)/$$dest include/asm/arch
else
        $(Q)if [ -d arch/$(ARCH)/mach-$(SOC)/include/mach ]; then       \
                dest=../../mach-$(SOC)/include/mach;                    \
        else                                                            \
                dest=arch-$(if $(SOC),$(SOC),$(CPU));                   \
        fi;                                                             \
        ln -fsn $$dest arch/$(ARCH)/include/asm/arch
endif
endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;223-配置过程总结&#34;&gt;2.2.3 配置过程总结&lt;/h3&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;2&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305242228329.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h2 id=&#34;23-编译过程详细分析imx6ull&#34;&gt;2.3 编译过程详细分析(IMX6ULL)&lt;/h2&gt;
&lt;h3 id=&#34;231-结论&#34;&gt;2.3.1 结论&lt;/h3&gt;
&lt;p&gt;以IMX6ULL为例，先说结论：&lt;/p&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;3&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305242231347.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;p&gt;细节如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;执行make命令时，要编译得到哪些文件由&lt;code&gt;ALL-y&lt;/code&gt;决定&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;规则如下&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)

_all : all

# Always append ALL so that arch config.mk&#39;s can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check

ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb

all:            $(ALL-y)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ALL-y&lt;/code&gt;的值为：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;checkarmreloc u-boot-dtb.imx u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check u-boot.dtb
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;u-boot-dtb.imx依赖于u-boot-dtb.bin，u-boot-dtb.bin又依赖于u-boot-nodtb.bin和dts/dt.dtb&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;各类u-boot文件都依赖于u-boot，先编译得到u-boot，它由&lt;code&gt;u-boot-init&lt;/code&gt;、&lt;code&gt;u-boot-main&lt;/code&gt;两部分链接而成&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;u-boot-init为：arch/arm/cpu/armv7/start.o&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;u-boot-main为：lib/built-in.o  fs/built-in.o  net/built-in.o 等等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如何编译各个built-in.o，以lib/built-in.o为例&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;执行: ``make -f ./scripts/Makefile.build obj=lib`&lt;/li&gt;
&lt;li&gt;会包含lib/Makefile，里面定义了&lt;code&gt;obj-y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用Makefile.build的规则把各个obj-y编译、链接为built-in.o&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用如下的命令链接得到u-boot：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;quiet_cmd_u-boot__ ?= LD      $@
      cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
      -T u-boot.lds $(u-boot-init)                             \
      --start-group $(u-boot-main) --end-group                 \
      $(PLATFORM_LIBS) -Map u-boot.map
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;再编译得到设备树文件: &lt;code&gt;make -f ./scripts/Makefile.build obj=dts dtbs&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;顶层Makefile如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;dts/dt.dtb: checkdtc u-boot&lt;br&gt;
&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;(&lt;/mo&gt;&lt;mi&gt;Q&lt;/mi&gt;&lt;mo&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;(Q)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34;&gt;Q&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;(MAKE) $(build)=dts dtbs&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
* scripts/Makefile.build中，会包含 dts/Makefile

```shell
DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb

$(DTB): arch-dtbs
$(obj)/dt.dtb: $(DTB) FORCE
      $(call if_changed,shipped)
targets += dt.dtb

$(DTB): arch-dtbs
	  $(Q)test -e $@ || (                                             \
	  echo &amp;gt;&amp;amp;2;                                                       \
	  echo &amp;gt;&amp;amp;2 &amp;quot;Device Tree Source is not correctly specified.&amp;quot;;      \
	  echo &amp;gt;&amp;amp;2 &amp;quot;Please define &#39;CONFIG_DEFAULT_DEVICE_TREE&#39;&amp;quot;;          \
	  echo &amp;gt;&amp;amp;2 &amp;quot;or build with &#39;DEVICE_TREE=&amp;lt;device_tree&amp;gt;&#39; argument&amp;quot;;  \
	  echo &amp;gt;&amp;amp;2;                                                       \
	  /bin/false)

arch-dtbs:
	  $(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs

.SECONDARY: $(obj)/dt.dtb.S

obj-$(CONFIG_OF_EMBED) := dt.dtb.o

dtbs: $(obj)/dt.dtb
	  @:
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后把u-boot、设备树打包: &lt;code&gt;make -f ./scripts/Makefile.build obj=arch/arm/imx-common u-boot-dtb.imx&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)

%.imx: %.bin
      $(Q)$(MAKE) $(build)=arch/arm/imx-common $@
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;232-顶层makefile&#34;&gt;2.3.2 顶层Makefile&lt;/h3&gt;
&lt;p&gt;顶层Makefile里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;包含架构相关的Makefile，里面指定了第1个文件（head-y）&lt;/li&gt;
&lt;li&gt;指定了要编译哪些子目录（libs-y）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从顶层Makefile开始分析：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
include arch/$(ARCH)/Makefile  # arch/arm/Makefile, 里面含有head-y libs-y

libs-y += lib/
libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
libs-$(CONFIG_OF_EMBED) += dts/
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
libs-y += drivers/dma/
libs-y += drivers/gpio/
libs-y += drivers/i2c/
libs-y += drivers/mmc/

libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)

libs-y := $(sort $(libs-y))

u-boot-dirs     := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples

u-boot-alldirs  := $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-))))

libs-y          := $(patsubst %/, %/built-in.o, $(libs-y))

u-boot-init := $(head-y)
u-boot-main := $(libs-y)

ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于&lt;code&gt;arch/arm/Makefile&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;head-y := arch/arm/cpu/$(CPU)/start.o

libs-y += arch/arm/cpu/$(CPU)/
libs-y += arch/arm/cpu/
libs-y += arch/arm/lib/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，顶层Makefile包含了&lt;code&gt;arch/arm/Makefile&lt;/code&gt;，确定了head-y为&lt;code&gt;arch/arm/cpu/armv7/start.o&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;还定义了libs-y变量，它里面含有多个目录。&lt;/p&gt;
&lt;h3 id=&#34;233-编译子目录&#34;&gt;2.3.3 编译子目录&lt;/h3&gt;
&lt;p&gt;顶层Makefile里定义了libs-y，会进入里面每一目录，使用它的Makefile进行编译：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;u-boot-main := $(libs-y)

$(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;

u-boot-dirs     := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples

$(u-boot-dirs): prepare scripts
        # make -f $(srctree)/scripts/Makefile.build obj=arch/arm/cpu/arm7
        $(Q)$(MAKE) $(build)=$@   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如：u-boot-main里面含有一个目录&amp;quot;arch/arm/cpu/armv7/&amp;quot;，在u-boot-dirs中它末尾的&amp;quot;/&amp;quot;被取消，就变成：arch/arm/cpu/armv7。&lt;/p&gt;
&lt;p&gt;然后使用scripts/Makefile.build进行处理：&lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;(&lt;/mo&gt;&lt;mi&gt;Q&lt;/mi&gt;&lt;mo&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;(Q)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34;&gt;Q&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;(MAKE) &lt;span class=&#34;katex&#34;&gt;&lt;span class=&#34;katex-mathml&#34;&gt;&lt;math&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;(&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mo&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;(build)=&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&#34;katex-html&#34; aria-hidden=&#34;true&#34;&gt;&lt;span class=&#34;base&#34;&gt;&lt;span class=&#34;strut&#34; style=&#34;height:1em;vertical-align:-0.25em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mopen&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34; style=&#34;margin-right:0.01968em;&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;mord mathdefault&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;mclose&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mspace&#34; style=&#34;margin-right:0.2777777777777778em;&#34;&gt;&lt;/span&gt;&lt;span class=&#34;mrel&#34;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;@ ，展开就是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;make -f scripts/Makefile.build obj=arch/arm/cpu/armv7/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;scripts/Makefile.build是编译u-boot源码的关键：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src := $(patsubst $(prefix)/%,%,$(obj))  # obj=arch/arm/cpu/arm7/, src=arch/arm/cpu/armv7

# 第1个目标
__build:

# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)  # 包含 arch/arm/cpu/arm7/Makefile

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a  # 如果包含的arch/arm/cpu/armv7/Makefile里定义了lib-y
endif

ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o # 如果包含的arch/arm/cpu/armv7/Makefile里定义了obj-y
endif


__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
         $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
         $(subdir-ym) $(always)
        @:


# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
        $(call cmd,force_checksrc)
        $(call if_changed_rule,cc_o_c)
        

ifdef builtin-target
quiet_cmd_link_o_target = LD      $@
# If the list of objects to link is empty, just create an empty built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
                      $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
                      $(cmd_secanalysis),\
                      rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)

$(builtin-target): $(obj-y) FORCE
        $(call if_changed,link_o_target)

targets += $(builtin-target)
endif # builtin-target
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;234-链接得到u-boot&#34;&gt;2.3.4 链接得到u-boot&lt;/h3&gt;
&lt;p&gt;顶层Makefile里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;libs-y          := $(patsubst %/, %/built-in.o, $(libs-y))

u-boot-init := $(head-y)
u-boot-main := $(libs-y)

# Rule to link u-boot
# May be overridden by arch/$(ARCH)/config.mk
quiet_cmd_u-boot__ ?= LD      $@
      cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
      -T u-boot.lds $(u-boot-init)                             \
      --start-group $(u-boot-main) --end-group                 \
      $(PLATFORM_LIBS) -Map u-boot.map

quiet_cmd_smap = GEN     common/system_map.o
cmd_smap = \
        smap=`$(call SYSTEM_MAP,u-boot) | \
                awk &#39;$$2 ~ /[tTwW]/ {printf $$1 $$3 &amp;quot;\\\\000&amp;quot;}&#39;` ; \
        $(CC) $(c_flags) -DSYSTEM_MAP=&amp;quot;\&amp;quot;$${smap}\&amp;quot;&amp;quot; \
                -c $(srctree)/common/system_map.c -o common/system_map.o

u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
        $(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
        $(call cmd,smap)
        $(call cmd,u-boot__) common/system_map.o
endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是把head-y和各个libs-y下的built-in.o链接得到u-boot。&lt;/p&gt;
&lt;h3 id=&#34;235-映像文件的依赖&#34;&gt;2.3.5 映像文件的依赖&lt;/h3&gt;
&lt;p&gt;对于IMX6ULL，要使用的是u-boot-dtb.imx，它含有u-boot和设备树。&lt;/p&gt;
&lt;p&gt;在顶层Makefile里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)

%.imx: %.bin
        $(Q)$(MAKE) $(build)=arch/arm/imx-common $@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;u-boot-dtb.imx依赖于u-boot-dtb.bin，u-boot-dtb.bin又依赖于u-boot-nodtb.bin和dts/dt.dtb。&lt;/p&gt;
&lt;p&gt;在顶层Makefile里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
        $(call if_changed,cat)

u-boot-nodtb.bin: u-boot FORCE
        $(call if_changed,objcopy)
        $(call DO_STATIC_RELA,$&amp;lt;,$@,$(CONFIG_SYS_TEXT_BASE))
        $(BOARD_SIZE_CHECK)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，编译出u-boot后，会先制作u-boot-nodtb.bin；然后编译dts/dt.dtb；最后生成u-boot-dtb.bin、imx文件：&lt;/p&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;4&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305242231347.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h3 id=&#34;236-编译设备树&#34;&gt;2.3.6 编译设备树&lt;/h3&gt;
&lt;p&gt;顶层Makefile里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;dts/dt.dtb: checkdtc u-boot
	  # make -f $(srctree)/scripts/Makefile.build obj=dts  dtbs
      $(Q)$(MAKE) $(build)=dts dtbs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要用到scripts/Makefile.build中，它会包含 dts/Makefile， dts/Makefile中含有dtbs目标：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;DEVICE_TREE ?= $(CONFIG_DEFAULT_DEVICE_TREE:&amp;quot;%&amp;quot;=%)
DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb # 就是 arch/arm/dts/imx6ull-14x14-evk.dtb

$(obj)/dt.dtb: $(DTB) FORCE
        $(call if_changed,shipped)

targets += dt.dtb

$(DTB): arch-dtbs
        $(Q)test -e $@ || (                                             \
        echo &amp;gt;&amp;amp;2;                                                       \
        echo &amp;gt;&amp;amp;2 &amp;quot;Device Tree Source is not correctly specified.&amp;quot;;      \
        echo &amp;gt;&amp;amp;2 &amp;quot;Please define &#39;CONFIG_DEFAULT_DEVICE_TREE&#39;&amp;quot;;          \
        echo &amp;gt;&amp;amp;2 &amp;quot;or build with &#39;DEVICE_TREE=&amp;lt;device_tree&amp;gt;&#39; argument&amp;quot;;  \
        echo &amp;gt;&amp;amp;2;                                                       \
        /bin/false)

arch-dtbs:
        $(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs

.SECONDARY: $(obj)/dt.dtb.S

obj-$(CONFIG_OF_EMBED) := dt.dtb.o

dtbs: $(obj)/dt.dtb
        @:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后会使用&lt;code&gt;$(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs&lt;/code&gt;来处理，进入：使用arch/arm/dts/Makefile，里面定了dtb-y：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;dtb-$(CONFIG_MX6) += imx6ul-14x14-ddr3-arm2.dtb \
        imx6ul-14x14-ddr3-arm2-emmc.dtb \
        imx6ul-14x14-ddr3-arm2-gpmi-weim.dtb \
        imx6ul-14x14-lpddr2-arm2.dtb \
        imx6ul-14x14-evk.dtb \
        imx6ul-14x14-evk-emmc.dtb \

PHONY += dtbs
dtbs: $(addprefix $(obj)/, $(dtb-y))
        @:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要编译出各类dtb文件，用到scripts/Makefile.lib，规则如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;quiet_cmd_dtc = DTC     $@
# Modified for U-Boot
# Bring in any U-Boot-specific include after the &#39;/dts-v1/;&#39; header
cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
        cat $&amp;lt; $(if $(u_boot_dtsi),\
                | sed &#39;/^\/ {$$/{x;s%$$%\#include \&amp;quot;$(u_boot_dtsi)\&amp;quot;%;G;}&#39;) | \
                $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) - ; \
        $(DTC) -O dtb -o $@ -b 0 \
                -i $(dir $&amp;lt;) $(DTC_FLAGS) \
                -d $(depfile).dtc.tmp $(dtc-tmp) ; \
        cat $(depfile).pre.tmp $(depfile).dtc.tmp &amp;gt; $(depfile)

$(obj)/%.dtb: $(src)/%.dts FORCE
        $(call if_changed_dep,dtc)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;237-制作映像文件&#34;&gt;2.3.7 制作映像文件&lt;/h3&gt;
&lt;p&gt;当制作出设备树文件后，在顶层Makefile里有如下规则，进而制作出u-boot-dtb.bin：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
        $(call if_changed,cat)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;回到顶层Makefile，看看如何制作imx映像文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)

%.imx: %.bin
        $(Q)$(MAKE) $(build)=arch/arm/imx-common $@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在u-boot-dtb.imx的依赖文件u-boot-dtb.bin已经生成了，将会使用arch/arm/imx-common/Makefile来生产imx文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;u-boot.imx: u-boot.bin $(IMX_CONFIG) $(PLUGIN).bin FORCE
        $(call if_changed,mkimage)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后会使用如下命令生产imx映像文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;./tools/mkimage -n board/freescale/mx6ullevk/imximage.cfg.cfgtmp -T imximage -e 0x87800000 -d u-boot-dtb.bin u-boot-dtb.imx
&lt;/code&gt;&lt;/pre&gt;
">uboot编译过程学习</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/wq0nSn5jN/"" data-c="
          &lt;h3 id=&#34;概述&#34;&gt;概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;虚拟文件系统&lt;/strong&gt;&lt;br&gt;
虚拟文件系统又称为虚拟文件系统转换（VFS，Virual FileSystem Switch）。VFS称为内核的子系统，VFS提供一个统一的接口，一个具体文件系统想要被Linux支持，就必须按照这个接口编写自己的操作函数，也将自己的细节对内核其他子系统隐藏起来。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文件系统分类&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;对磁盘数据做索引&lt;/strong&gt;&lt;br&gt;
fat32，nfs，ext3/ext4，ntfs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件系统做索引&lt;/strong&gt;&lt;br&gt;
tfs，gfs，fastdfs （这个三个为分布式文件系统）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fuse内核的一部分&lt;/strong&gt;&lt;br&gt;
为文件添加时间戳，特征描述等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VFS&lt;/strong&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305202220341.png&#34; alt=&#34;VFS调用架构&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;vfs主要作用&#34;&gt;VFS主要作用&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1. 支持多种九天文件系统之间的相互访问；
2. 接受用户层的系统调用，比如：open,link,write等；
3. 对具体文件系统的数据结构进行抽象，以一种统一的数据结构进行管理；
4. 接受内核其他子系统的操作请示。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;vfs系统架构图&#34;&gt;VFS系统架构图&lt;/h3&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;1&#34;&gt;&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305210939299.png&#34; alt=&#34;VFS系统架构图&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
&lt;h3 id=&#34;vfs在实际系统调用的常用操作&#34;&gt;VFS在实际系统调用的常用操作&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;mount()/umount()：文件系统的挂载和卸载&lt;br&gt;
sysfs()：查看系统文件的信息&lt;br&gt;
chroot()：更改 root 目录，修改根文件目录&lt;br&gt;
chdir()/fchdir()/getcwd()：切换当前工作目录到指定的路径&lt;br&gt;
mkdir()/rmdir()：文件夹创建删除&lt;br&gt;
readlink()/symlink()：文件软链接&lt;br&gt;
chown()/fchown()/lchown()：文件权限，用户组&lt;br&gt;
select()/poll()：文件异步通知访问&lt;br&gt;
flock()：文件锁&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;VFS并不是每次都需要open或close关闭资源，有时可以通过修改访问文件的指针到已经打开的文件资源上，实现资源访问。&lt;/p&gt;
&lt;h3 id=&#34;vfs核心数据结构对象&#34;&gt;VFS核心数据结构(对象)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;存放在内存中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;超级块（superblock）对象&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数据结构：双向循环链表&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;记录整个 filesystem 相关信息的地方， 没有 Superblock ，就没有这个 filesystem 了。&lt;/li&gt;
&lt;li&gt;一个超级块对应一个文件系统(已经安装的文件系统类型如ext2，此处是实际的文件系统，不是VFS)。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;block 与 inode 的总量；&lt;/li&gt;
&lt;li&gt;未使用与已使用的 inode / block 数量；&lt;/li&gt;
&lt;li&gt;block 与 inode 的大小 (block 为 1, 2, 4K，inode 为 128 bytes)；&lt;/li&gt;
&lt;li&gt;filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息；&lt;/li&gt;
&lt;li&gt;一个 valid bit 数值，若此文件系统已被挂载，则 valid bit 为 0 ，若未被挂载，则 valid bit 为 1 。
&lt;blockquote&gt;
&lt;p&gt;Superblock 是非常重要的，因为文件系统的基本信息都写在这里， superblock 的大小为 1024bytes，此外，每个 block group 都可能含有 superblock ！事实上除了第一个 block group 内会含有 superblock 之外，后续的 block group 不一定含有 superblock ， 而若含有 superblock 则该 superblock 主要是做为第一个 block group 内 superblock 的备份咯，这样可以进行 superblock 的救援。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结构图&lt;/strong&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305211027914.png&#34; alt=&#34;superblock块的结构图&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;块（block）对象&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原则上，block 的大小与数量在格式化完就不能够再改变了(除非重新格式化)；&lt;/li&gt;
&lt;li&gt;每个 block 内最多只能够放置一个文件的数据；&lt;/li&gt;
&lt;li&gt;承上，如果文件大于 block 的大小，则一个文件会占用多个 block 数量；&lt;/li&gt;
&lt;li&gt;承上，若文件小于 block ，则该 block 的剩余容量就不能够再被使用了(磁盘空间会浪费)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;索引节点（inode）对象&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;索引节点：存放在磁盘，属于静态数据结构&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;用于存储文件的各属性，包括：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;所有者信息：文件的owner，group；&lt;/li&gt;
&lt;li&gt;权限信息：read、write和excite；&lt;/li&gt;
&lt;li&gt;该文件真正内容的指向 (pointer)；&lt;/li&gt;
&lt;li&gt;时间信息：建立或改变时间（ctime）、最后读取时间（atime）、最后修改时间（mtime）；&lt;/li&gt;
&lt;li&gt;定义文件特性的旗标(flag)，如 SetUID...；&lt;/li&gt;
&lt;li&gt;内容信息：type，size，以及相应的block的位置信息。&lt;/li&gt;
&lt;li&gt;该文件真正内容的指向 (pointer)；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;注意：不记录文件名或目录名，文件名或目录名记录在文件所在目录对应的block里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;inode和文件的关系?&lt;/strong&gt;
&lt;blockquote&gt;
&lt;p&gt;当创建一个文件的时候，就给文件分配了一个 inode。一个 inode 只对应一个实际文件，一个文件也会只有一个 inode。inodes 最大数量就是文件的最大数量。&lt;br&gt;
注意管理inode的四个链表：&lt;br&gt;
&lt;code&gt;static struct hlist_head *inode_hashtable __read_mostly;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;目录项（dentry）对象&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;目录项是描述文件的逻辑属性，只存在于内存中，并没有实际对应的磁盘上的描述，更确切的说是存在于内存的目录项缓存，为了提高查找性能而设计。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注意不管是文件夹还是最终的文件，都是属于目录项，所有的目录项在一起构成一颗庞大的目录树。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;例如：open一个文件/home/xxx/yyy.txt，那么/、home、xxx、yyy.txt都是一个目录项，VFS在查找的时候，根据一层一层的目录项找到对应的每个目录项的inode，那么沿着目录项进行操作就可以找到最终的文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个有效的dentry结构必定有一个inode结构，这是因为一个目录项要么代表着一个文件，要么代表着一个目录，而目录实际上也是文件。所以，只要dentry结构是有效的，则其指针d_inode必定指向一个inode结构。但是inode却可以对应多个。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注意：目录也是一种文件(所以也存在对应的inode)。打开目录，实际上就是打开目录文件。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文件（file）对象&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文件对象描述的是进程已经打开的文件。因为一个文件可以被多个进程打开，所以一个文件可以存在多个文件对象。但是由于文件是唯一的，那么&lt;strong&gt;inode就是唯一&lt;/strong&gt;的，&lt;strong&gt;目录项也是定的&lt;/strong&gt;！&lt;/li&gt;
&lt;li&gt;进程其实是通过文件描述符来操作文件的，每个文件都有一个32位的数字来表示下一个读写的字节位置，这个数字叫做文件位置。&lt;/li&gt;
&lt;li&gt;一般情况下打开文件后，打开位置都是从0开始，除非一些特殊情况。Linux用file结构体来保存打开的文件的位置，所以file称为打开的文件描述。file结构形成一个&lt;strong&gt;双链表&lt;/strong&gt;，称为系统打开文件表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;file参考源码&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-c/c++&#34;&gt;    struct file {
    union {
        struct llist_node   fu_llist; /* 每个文件系统中被打开的文件都会形成一个双链表 */
        struct rcu_head     fu_rcuhead;
    } f_u;
    struct path     f_path;
    #define f_dentry    f_path.dentry 782     struct inode  *f_inode;   /* cached value */
    const struct file_operations    *f_op; /* 指向文件操作表的指针 */
    
    /*
    * Protects f_ep_links, f_flags.
    * Must not be taken from IRQ context.
    */
    spinlock_t      f_lock;
    atomic_long_t       f_count;  /* 文件对象的使用计数 */
    unsigned int        f_flags;  /* 打开文件时所指定的标志 */
    fmode_t         f_mode;       /* 文件的访问模式(权限等) */
    struct mutex        f_pos_lock;
    loff_t          f_pos;       /* 文件当前的位移量 */
    struct fown_struct  f_owner;
    const struct cred   *f_cred;
    struct file_ra_state    f_ra; /* 预读状态 */

    u64         f_version;   /* 版本号 */
    #ifdef CONFIG_SECURITY 801     void            *f_security;  /* 安全模块 */
    #endif 803     /* needed for tty driver, and maybe others */
        void            *private_data; /* 私有数据 */

    #ifdef CONFIG_EPOLL 807     /* Used by fs/eventpoll.c to link all the hooks to this file */
        struct list_head    f_ep_links;
        struct list_head    f_tfile_llink;
    #endif /* #ifdef CONFIG_EPOLL */
        struct address_space    *f_mapping;/* 页缓存映射 */
    #ifdef CONFIG_DEBUG_WRITECOUNT 813     unsigned long f_mnt_write_state;
    #endif 815 } __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */     
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参考链接：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;原文链接：https://blog.csdn.net/daocaokafei/article/details/112728887&lt;/li&gt;
&lt;li&gt;YiYezc博客：https://www.cnblogs.com/yiyezc/p/5208318.html&lt;/li&gt;
&lt;li&gt;长烟慢慢：https://blog.csdn.net/cymm_liu/article/details/36006393&lt;/li&gt;
&lt;li&gt;linux技术栈： https://zhuanlan.zhihu.com/p/411051334&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;文件系统挂载工具mount&#34;&gt;文件系统挂载工具（mount）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;mount工具如何走到系统调用&lt;/strong&gt;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;mount -t proc proc /mnt/
&lt;/code&gt;&lt;/pre&gt;
&lt;strong&gt;调用流程：&lt;/strong&gt;
&lt;blockquote&gt;
&lt;p&gt;syscall -- &amp;gt; mount&lt;br&gt;
do_mount&lt;br&gt;
do_new_mount&lt;br&gt;
vfs_kern_mount&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305202255411.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;补充&#34;&gt;补充&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;文件系统结构&lt;/strong&gt;&lt;br&gt;
文件系统（重要的结构体）：
&lt;ol&gt;
&lt;li&gt;file_system_type：描述文件系统一些标识&lt;/li&gt;
&lt;li&gt;super_block：通过这个块衍射出新的子块&lt;/li&gt;
&lt;li&gt;file/file_operations&lt;/li&gt;
&lt;li&gt;inode/inode_operations&lt;/li&gt;
&lt;li&gt;dentry&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
">linux中VFS</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/GTICcd6rB/"" data-c="
          &lt;!-- more --&gt;
&lt;h2 id=&#34;概述&#34;&gt;概述&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;fuse是什么？&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fuse可以用来做什么？&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fuse调用VFS文件系统架构的逻辑图&lt;/strong&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305201815338.jpg&#34; alt=&#34;fuse调用VFS文件系统架构&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;资源&#34;&gt;资源&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;libfuse库文件&lt;/li&gt;
&lt;/ol&gt;
">基于fuse实现自定义用户态文件系统</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/U5o_FqRYG/"" data-c="
          &lt;h2 id=&#34;1-什么负载因子loadfactor&#34;&gt;1. 什么负载因子（loadFactor）?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;负载因子（loadFactor）：当存储的数据占总容量的比例，取值范围（0,1）&lt;br&gt;
容量（capacity）：hashMap可以存储多少个数据，取值范围[0，MAX_VALUE]，MAX_VALUE:为计算机最大整数值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HashMap中数据量存储扩容机制&lt;/strong&gt;&lt;br&gt;
第一次创建 HashMap 的时候，就会指定其容量（如果未明确指定，默认是 16），那随着我们不断的向 HashMap 中 put 元素的时候，若数据超过某个阀值（threshold）时，就需要提前对HashMap申请一些新的空间存储数据，存储未来可能需要put的元素。
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;阀值如何确定呢？&lt;/strong&gt;&lt;br&gt;
阀值（threshold） = 总容量（capacity） x 负载因子（loadFactor）&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HashMap扩容具体示例&lt;/strong&gt;&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size &amp;gt;= threshold) &amp;amp;&amp;amp; (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    createEntry(hash, key, value, bucketIndex);
}
&lt;/code&gt;&lt;/pre&gt;
从代码中我们可以看到，在向 HashMap 中添加元素过程中，如果 元素个数（size）超过临界值（threshold） 的时候，就会进行自动扩容（resize），并且，在扩容之后，还需要对 HashMap 中原有元素进行 rehash，即将原来桶中的元素重新分配到新的桶中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;2hashmap为什么要扩容呢&#34;&gt;2.HashMap为什么要扩容呢？&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;  提示：HashMap 在扩容到过程中不仅要对其容量进行扩充，还需要进行 rehash！所以，这个过程其实是很耗时的，并且 Map 中元素越多越耗时。
  rehash 的过程相当于对其中所有的元素重新做一遍 hash，重新计算要分配到那个桶中。
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;为什么还要扩容呢？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;原因：哈希冲突&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们知道，HashMap 其实是底层基于哈希函数实现的，但是哈希函数都有如下一个基本特性：根据同一哈希函数计算出的哈希值如果不同，那么输入值肯定也不同。但是，根据同一哈希函数计算出的哈希值如果相同，输入值不一定相同。&lt;br&gt;
两个不同的输入值，根据同一哈希函数计算出的哈希值相同的现象叫做碰撞。&lt;br&gt;
衡量一个哈希函数的好坏的重要指标就是发生碰撞的概率以及发生碰撞的解决方案。&lt;br&gt;
而为了解决哈希碰撞，有很多&lt;a href=&#34;https://www.jianshu.com/p/4d3cb99d7580&#34;&gt;办法&lt;/a&gt;，HashMap采用数组+链表方法。如下图：&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305201153911.png&#34; alt=&#34;hashMap底层原理图&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;如何避免哈希冲突？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;哈希冲突的原因：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;容量太小。容量小，碰撞的概率就高了。狼多肉少，就会发生争抢&lt;/li&gt;
&lt;li&gt;hash 算法不够好。算法不合理，就可能都分到同一个或几个桶中。分配不均，也会发生争抢。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解决方案&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/gongcheng-/p/10894205.html&#34;&gt;经典hash冲突解决参考方法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;针对HashMap来说，上述两种原因的解决方法相结合，在合适的时候扩大数组容量，再通过一个合适的 hash 算法计算元素分配到哪个数组中，就可以大大的减少冲突的概率，就能避免查询效率低下的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;3为什么默认负载因子loadfactor是075&#34;&gt;3.为什么默认负载因子（loadFactor）是0.75？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JDK官方文档描述&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;As a general rule, the default load factor (.75) offers a good tradeoff 
between time and space costs. Higher values decrease the space overhead 
but increase the lookup cost (reflected in most of the operations of the 
HashMap class, including get and put).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;意思： 一般来说，默认的负载因子 (0.75) 在时间和空间成本之间提供了很好的权衡。更高的值减少了空间开销，但增加了查找成本(反映在 HashMap 类的大多数操作中，包括 get 和 put)。    &lt;br&gt;&lt;br&gt;
其他原因：还有个原因是计算机使用二进制，为了保证负载因子（loadFactor） * 容量（capacity）的结果是一个整数，则就需要找与2的幂乘积结果都是整数的负载因子，这值是 0.75(3/4)且接近于log(2),故比较合理。参考自&lt;a href=&#34;https://www.jianshu.com/p/effb601f2c48&#34;&gt;原文&lt;/a&gt;&lt;br&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;若负载因子为1呢？&lt;/strong&gt;&lt;br&gt;
如果我们把负载因子设置成 1，容量使用默认初始值 16，那么表示一个 HashMap在存储空间用完之后才会进行扩容。那么在 HashMap 中，最好的情况是这 16 个元素通过 hash 算法之后分别落到了 16 个不同的桶中，否则就必然发生哈希碰撞。而且随着元素越多，哈希碰撞的概率越大，查找速度也会越低。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
">java中HashMap的负载因子</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/imx6ull-de-uboot-he-kernel-bian-yi-qi-dong/"" data-c="
          &lt;h2 id=&#34;umx6ull&#34;&gt;umx6ull&lt;/h2&gt;
&lt;h3 id=&#34;资料准备&#34;&gt;资料准备&lt;/h3&gt;
&lt;p&gt;imx6ull现在已经将源代码放到github仓库了，&lt;code&gt;source.codeaurora.org&lt;/code&gt;这个已经弃用了&lt;br&gt;
代码迁移到github的解释：&lt;a href=&#34;https://community.nxp.com/t5/i-MX-Processors/source-codeaurora-org-is-closed-where-is-the-nxp-source-code-how/m-p/1627753#M203723&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
git仓库地址：&lt;a href=&#34;https://github.com/nxp-imx&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;内核编译详细说明&#34;&gt;内核编译详细说明&lt;/h3&gt;
&lt;p&gt;关于内核编译共性，详细说明，请参考&lt;a href=&#34;../linux_kernel_compiler&#34;&gt;内核编译这篇文章&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;kernel编译&#34;&gt;kernel编译&lt;/h3&gt;
&lt;h4 id=&#34;编译内核文件&#34;&gt;编译内核文件&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    # 设置自己开发版对应的cpu架构，arm或其他（也可以直接配置到环境变量中）
    export ARCH=arm
    export CROSS_COMPILE=arm-none-linux-gnueabihf-
    export PATH=$PATH:交叉编译工具的bin目录 （如：/home/software/tools/gcc-arm-10.2-2020.11-aarch64-arm-none-linux-gnueab/bin）
    # 切换到内核源码根目录
    cd linux-imx
    make mrproper   #移除之前编译生成的文件和配置文件，备份文件等
    # 制作开发板对应的配置文件
    # defconfig：一般位于linux-4.9.88/arch/$ARCH/configs/这个目录下
    make imx_v7_defconfig
    # 使用make menuconfig，对make imx_v7_defconfig进行调整修改
    make menuconfig     （可选）

    # 编译内核：编译得到的输出文件在linux-4.9.88/arch/$ARCH/boot/目录下
    make zImage -j4
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&#34;错误解决&#34;&gt;错误解决&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;问题一
&lt;blockquote&gt;
&lt;p&gt;/usr/bin/ld: scripts/dtc/dtc-parser.tab.o:(.bss+0x10): multiple definition of `yylloc&#39;; scripts/dtc/dtc-lexer.lex.o:(.bss+0x0): first defined here collect2: error: ld returned 1 exit status&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;问题原因：
&lt;blockquote&gt;
&lt;p&gt;gcc版本过高&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;解决方案：
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;scripts/dtc/dtc-lexer.lex.c文件进行编辑，找到&lt;code&gt;YYLTYPE yylloc&lt;/code&gt; 在前面添加&lt;code&gt;extern&lt;/code&gt;关键字，保存退出，然后执行&lt;code&gt;make clean&lt;/code&gt; 清除之前编译的文件，再执行&lt;code&gt;make&lt;/code&gt;进行编译&lt;/li&gt;
&lt;li&gt;scripts/dtc/dtc-lexer.lex.c文件，位于当前编译的内核源码目录的根目录中&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;问题二
&lt;blockquote&gt;
&lt;p&gt;/bin/sh: 1: lzop：not found&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;问题原因：&lt;br&gt;
没有lzop库&lt;/li&gt;
&lt;li&gt;解决方案：&lt;br&gt;
&lt;code&gt;sudo apt install lzop&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;补充说明&#34;&gt;补充说明&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; make Image，make zImage，make uImage的区别与解释说明，[点击此处]( https://blog.csdn.net/LEON1741/article/details/54809347)
&amp;gt; make zImage -j4：中`-j4`表示指定4个内核进行编译速度比较快（不是越多越好，具体根据自己的开发板选择）
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;编译设备树&#34;&gt;编译设备树&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    make dtbs
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;编译模块驱动&#34;&gt;编译模块驱动&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    # 编译模块
    make modules
    # 安装模块
    make INSTALL_MOD_PATH=module的path modules_install
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;uboot编译&#34;&gt;uboot编译&lt;/h3&gt;
">开发板的uboot和kernel编译启动</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/qemu-shi-yong/"" data-c="
          &lt;h2 id=&#34;为什么要用qemu&#34;&gt;为什么要用QEMU？&lt;/h2&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li&gt;可以简化操作专注于重要的linux驱动框架开发&lt;/li&gt;
&lt;li&gt;可以通过debug调试学习开发&lt;/li&gt;
&lt;li&gt;尽量节约在硬件框架的时间&lt;br&gt;
linux驱动 = 驱动框架 + 硬件操作&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;使用qemu模拟系统&#34;&gt;使用QEMU模拟系统&lt;/h2&gt;
&lt;hr&gt;
&lt;h3 id=&#34;启动虚拟机模拟x86_64的linux系统&#34;&gt;启动虚拟机，模拟x86_64的linux系统&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;qemu-system-x86_64 -smp 2 -m 1024M -kernel /home/learn/software/kernel/linux-4.14.314/arch/x86_64/boot/bzImage -nographic -append &amp;quot;root=/dev/ram console=ttyS0 init=/linuxrc&amp;quot; -initrd /home/learn/software/busybox/x86/test/busybox-1.32.1/rootfs_ext3.img.gz   -serial file:output.txt        
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;参数解释说明&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;append：类似java程序的main方法的参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;root=/dev/ram：表示采用内存虚拟启动盘，ramdisk&lt;/li&gt;
&lt;li&gt;init=/linuxrc：内核初始化文件（内核启动执行的第一个文件），一般为&lt;code&gt;etc/init&lt;/code&gt;可执行文件，busybox为&lt;code&gt;/linuxrc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;console=ttyS0：打印信息输出的控制台&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;kernel：指定内核文件路径&lt;br&gt;
initrd：指定根文件系统路径&lt;br&gt;
nographic：不使用图形化界面&lt;br&gt;
serial：串口输出信息&lt;br&gt;
smp：指定内核个数&lt;br&gt;
m：开辟内存大小&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;问题排查&#34;&gt;问题排查&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;当我使用QEMU去运行ubuntu22.04编译linux内核4.9.88时出现了以下问题&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/1684077515747.jpg&#34; alt=&#34;linux内核总是闪烁”decompressing linux...“&#34; loading=&#34;lazy&#34;&gt;
&lt;ul&gt;
&lt;li&gt;问题描述&lt;br&gt;
原因可能ubuntu18以后的系统在编译老版本内核（&amp;lt;5.0）时可能缺少某些库文件，导致编译后的内核不能运行，具体的暂时没有找到。(感谢这个&lt;a href=&#34;https://vccolombo.github.io/cybersecurity/linux-kernel-qemu-stuck/&#34;&gt;vccolombo博客&lt;/a&gt;，这篇博客非常好)&lt;/li&gt;
&lt;li&gt;解决方法
&lt;ul&gt;
&lt;li&gt;使用ubuntu18版本编译内核，然后拷贝到ubuntu22上运行&lt;/li&gt;
&lt;li&gt;用ubuntu22编译更新版本的linux内核。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;qemu启动后，出现CPU panic的解决方案
&lt;ul&gt;
&lt;li&gt;问题描述&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305152047171.png&#34; alt=&#34;cpu panic：umount fileSystem...&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;解决方法
&lt;ul&gt;
&lt;li&gt;根文件系统制作失败，可能是挂载的设备块&lt;code&gt;/dev/loop&lt;/code&gt;内存空间不足。&lt;/li&gt;
&lt;li&gt;根文件系统制作镜像时，没有挂载对应的设备卷&lt;code&gt;/dev/loop0&lt;/code&gt;,导致没有空间进行分配，进而挂载失败&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    mount -o loop :在内部默认的是将文件和 /dev/loop0 挂载起来了。并不能适用于所有的场景，比如，我们想创建一个硬盘文件，然后对该文件进行分区，接着挂载其中一个子分区，这时就不能用 -o loop 这种方法了。因此必须如下做。
    losetup /dev/loop1 loopfile.img
    fdisk /dev/loop1
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
">QEMU使用</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/linux-nei-he-yi-zhi/"" data-c="
          &lt;h2 id=&#34;linux内核基础知识&#34;&gt;linux内核基础知识&lt;/h2&gt;
&lt;h3 id=&#34;linux内核目录&#34;&gt;linux内核目录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;du -sh&lt;/code&gt;查看目录大小&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;arch/&lt;/code&gt;：存放不同cpu体系结构的代码&lt;br&gt;
&lt;code&gt;arch/boot/&lt;/code&gt;：arm平台启动相关代码&lt;br&gt;
&lt;code&gt;mach-efm32/&lt;/code&gt;：不同硬件公司基于arm平台的硬件平台代码&lt;br&gt;
&lt;code&gt;Documentation/&lt;/code&gt;：针对内核的文档说明&lt;br&gt;
&lt;code&gt;firmware/&lt;/code&gt;：固件代码，如：WiFi，蓝牙模块的固件代码&lt;br&gt;
&lt;code&gt;init/&lt;/code&gt;：相当于c语言main方法，&lt;code&gt;init/main.c&lt;/code&gt;内核初始化程序代码，里面的&lt;code&gt;star_kernel&lt;/code&gt;为入口函数&lt;br&gt;
&lt;code&gt;init/\*\*_mount_\*\*.c&lt;/code&gt;：这些文件负责系统启动后文件系统的挂载&lt;br&gt;
&lt;code&gt;kconfig文件&lt;/code&gt;：内核编译系统相关的文件&lt;br&gt;
&lt;code&gt;security/&lt;/code&gt;：安全相关代码&lt;br&gt;
&lt;code&gt;usr/&lt;/code&gt;：测试相关代码&lt;br&gt;
&lt;code&gt;block/&lt;/code&gt;：块设备相关代码，如：内存存储等&lt;br&gt;
&lt;code&gt;drivers/&lt;/code&gt;：驱动相关代码，占据内核代码一半左右，（驱动开发常用），做IC集成要求基本这个目录下的文件都懂一些&lt;br&gt;
&lt;code&gt;fs/&lt;/code&gt;：文件系统相关代码，内核引导的文件系统，还有ext3，ext4，ntfs，fat32等&lt;br&gt;
&lt;code&gt;ipc/&lt;/code&gt;：进程通信相关代码，如：message，管道通信等&lt;br&gt;
&lt;code&gt;kernel/&lt;/code&gt;：linux系统中所有体系架构通用的相关代码，如：进程&lt;br&gt;
&lt;code&gt;arch/arm/kernel&lt;/code&gt;：arm体系相关的通用代码&lt;br&gt;
&lt;code&gt;net/&lt;/code&gt;：网络通信相关的代码&lt;br&gt;
&lt;code&gt;sound/&lt;/code&gt;：声卡相关的代码&lt;br&gt;
&lt;code&gt;crypto/&lt;/code&gt;：加密解密相关代码&lt;br&gt;
&lt;code&gt;include/&lt;/code&gt;：内核头文件相关的代码&lt;br&gt;
&lt;code&gt;lib/&lt;/code&gt;：内核里面通用的库文件&lt;br&gt;
&lt;code&gt;mm/&lt;/code&gt;：内存管理相关代码，比较&lt;strong&gt;重要&lt;/strong&gt;，页目录表，页表，以及相关的管理等都在这里&lt;br&gt;
&lt;code&gt;scripts/&lt;/code&gt;：脚本文件，如：内核编译过程中的各个编译文件链接的相关脚本等&lt;br&gt;
&lt;code&gt;dd/&lt;/code&gt;：主要负责内核调试&lt;br&gt;
&lt;code&gt;tools/&lt;/code&gt;：工具&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;linux342内核移植&#34;&gt;linux3.4.2内核移植&lt;/h2&gt;
&lt;h3 id=&#34;框架介绍&#34;&gt;框架介绍&lt;/h3&gt;
">linux内核移植</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/hsk-cname/"" data-c="
          &lt;p&gt;将host主机想办法换成他原来的主机域名&lt;/p&gt;
&lt;p&gt;设置了访问规则，仅仅允许自己的域名及其子域名进入&lt;/p&gt;
&lt;p&gt;若想访问，则可以通过重定向的方式，但是要进行域名备案（流程比较麻烦，需要IPC码）&lt;/p&gt;
&lt;p&gt;若想进行访问，则要讲http请求头里的host改成花生的二级域名即可（仅仅是个人猜想，并未实践）&lt;/p&gt;
">花生壳的壳域名不能使用阿里域名进行CNAME</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/linux-ufw/"" data-c="
          &lt;h4 id=&#34;本文参考自点击此处&#34;&gt;本文参考自：&lt;a href=&#34;https://markdown.com.cn/basic-syntax/code.html&#34;&gt;点击此处&lt;/a&gt;&lt;/h4&gt;
&lt;h2 id=&#34;安装ufw&#34;&gt;安装UFW&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install ufw
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;检查ufw的状态&#34;&gt;检查UFW的状态&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw status verbose
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;ufw默认策略&#34;&gt;UFW默认策略&lt;/h2&gt;
&lt;p&gt;默认情况下，UFW 阻塞了所有进来的连接，并且允许所有出去的连接。这意味着任何人无法访问你的服务器，除非你打开端口。运行在服务器上的应用和服务可以访问外面的世界。&lt;/p&gt;
&lt;p&gt;默认的策略定义在&lt;code&gt;/etc/default/ufw&lt;/code&gt;文件中，并且可以通过使用&lt;code&gt;sudo ufw default &amp;lt;policy&amp;gt; &amp;lt;chain&amp;gt;&lt;/code&gt;命令来修改。&lt;/p&gt;
&lt;p&gt;防火墙策略是用来构建更多详细的和用户自定义的规则的基础。通常情况下，初始的默认策略是一个很好的起点。&lt;/p&gt;
&lt;h2 id=&#34;应用配置&#34;&gt;应用配置&lt;/h2&gt;
&lt;p&gt;大部分应用都附带一份应用配置，它描述了服务，并且包含了 UFW 设置。这个规则在软件包安装的时候，被自动创建在&lt;code&gt;/etc/ufw/applications.d&lt;/code&gt;目录下。&lt;/p&gt;
&lt;p&gt;想要列举出你系统上所有的应用配置，输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw app list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;与你系统上安装的软件包有关系，输出应该看起来像下面这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想要查找更多关于指定配置和包含规则的信息，使用下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-sudo&#34;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出应该显示&amp;quot;Nginx Full&amp;quot;配置打开了端口&amp;quot;80&amp;quot;和&amp;quot;443&amp;quot;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Small, but very powerful and efficient web server

Ports:
  80,443/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你也可以为你的应用创建自定义的配置。&lt;/p&gt;
&lt;h2 id=&#34;启用ufw&#34;&gt;启用UFW&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;注意：如果你在远程位置连接你的 Ubuntu，在启用 UFW 防火墙之前，你必须显式允许进来的 SSH 连接。否则，你将永远都无法连接到机器上。&lt;/strong&gt;&lt;br&gt;
现在 UFW 防火墙被配置允许 SSH 远程连接，启用它，输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Rules updated
Rules updated (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 SSH 运行在非标准端口，你需要打开这个端口。&lt;/p&gt;
&lt;p&gt;例如，如果你的 SSH 守护程序监听了&lt;code&gt;7722&lt;/code&gt;，输入下面的命令，允许连接通过那个端口：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow 7722/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在防火墙被配置允许进来的 SSH 连接，你可以输入下面的命令，启用它：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw enable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你将会被警告启用防火墙可能会中断现有的 SSH 连接，输入&amp;quot;y&amp;quot;，并且回车。&lt;/p&gt;
&lt;h2 id=&#34;打开端口&#34;&gt;打开端口&lt;/h2&gt;
&lt;p&gt;取决于运行在你服务器上的应用，你需要根据服务打开不同的端口。&lt;/p&gt;
&lt;p&gt;通用的打开端口的语法如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ufw allow port_number/protocol
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面是一些关于如何允许 HTTP 连接的方法。&lt;/p&gt;
&lt;p&gt;第一个选项就是使用服务名。UFW 检查&lt;code&gt;/etc/services&lt;/code&gt;文件，其中指定服务的端口和协议：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow http
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你也可以指定端口号和协议：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow 80/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当没有给出协议的时候，UFW 同时创建&lt;code&gt;tcp&lt;/code&gt;和&lt;code&gt;udp&lt;/code&gt;的规则。&lt;/p&gt;
&lt;p&gt;另外一个选项就是使用应用程序配置。在这个例子中，是&amp;quot;Nginx HTTP&amp;quot;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow &#39;Nginx HTTP&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UFW 还支持另外一种语法，使用 &lt;code&gt;proto&lt;/code&gt; 关键字来指定协议。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;端口范围&lt;br&gt;
UFW 允许你打开端口范围。使用分号分隔开端口的起点和终点，当你指定协议时，或者是 tcp，或者是udp。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如，如果你想允许端口从&lt;code&gt;7100&lt;/code&gt;到&lt;code&gt;7200&lt;/code&gt;，同时支持&lt;code&gt;tcp&lt;/code&gt;和&lt;code&gt;udp&lt;/code&gt;，你将要运行下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow 7100:7200/tcp
sudo ufw allow 7100:7200/udp
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;允许指定 IP 地址访问指定端口&lt;br&gt;
想要允许指定源 IP 的所有端口上的所有连接通过，使用&lt;code&gt;from&lt;/code&gt;关键字，加上源地址。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里是一个 IP 地址白名单的例子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow from 64.63.62.61
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你指向允许给定 IP 访问指定的端口，使用&lt;code&gt;to any port&lt;/code&gt;关键字加上端口号。&lt;/p&gt;
&lt;p&gt;例如，允许从 IP&lt;code&gt;64.63.62.61&lt;/code&gt;的机器，通过&lt;code&gt;22&lt;/code&gt;端口访问,输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow from 64.63.62.61 to any port 22
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;7.3 允许子网&lt;br&gt;
允许一个子网 IP 地址的访问和允许一个单个 IP 地址的访问，命令是一样的。唯一的不同是需要指定网络掩码。&lt;/p&gt;
&lt;p&gt;下面是一个例子，显示如何允许 IP 地址(192.168.1.1 到 192.168.1.254)，通过 3360(MySQL)，你可以使用这个命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow from 192.168.1.0/24 to any port 3306
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;7.4 允许指定网络接口的连接&lt;br&gt;
想要允许连接通过指定网络接口，使用&lt;code&gt;allow in on&lt;/code&gt; 和 网络接口的名字：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow in on eth2 to any port 3306
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;禁止连接&#34;&gt;禁止连接&lt;/h2&gt;
&lt;p&gt;对于所有进来连接的默认的策略被设置为&lt;code&gt;deny&lt;/code&gt;，如果你没有修改它，UFW 将会屏蔽所有进来的连接，除非你指定打开连接。&lt;/p&gt;
&lt;p&gt;写禁止规则和写允许规则是一样的，你需要的仅仅是使用&lt;code&gt;deny&lt;/code&gt;关键字替换&lt;code&gt;a&lt;/code&gt;llow`。&lt;/p&gt;
&lt;p&gt;比如说你打开了端口&lt;code&gt;80&lt;/code&gt;和&lt;code&gt;443&lt;/code&gt;，并且你的服务器处于来自&lt;code&gt;23.24.25.0/24&lt;/code&gt;网络的攻击。想要禁止来自&lt;code&gt;23.24.25.0/24&lt;/code&gt;的所有连接，使用下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw deny from 23.24.25.0/24
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里是一个例子，关于禁止从&lt;code&gt;23.24.25.0/24&lt;/code&gt;对&lt;code&gt;80&lt;/code&gt;和&lt;code&gt;443&lt;/code&gt;端口的访问，你可以使用下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw deny proto tcp from 23.24.25.0/24 to any port 80,443
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;删除ufw规则&#34;&gt;删除UFW规则&lt;/h2&gt;
&lt;p&gt;有两种不同的方式可以删除 UFW 规则。通过规则序号和通过指定的规则。&lt;/p&gt;
&lt;p&gt;通过规则序号来删除 UFW 规则很简单，特别是你刚接触 UFW。&lt;/p&gt;
&lt;p&gt;想要通过规则序号来删除，你需要找到你想删除的规则序号。想要这么做，运行下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw status numbered
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22/tcp                     ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 8080/tcp                   ALLOW IN    Anywhere
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想要删除规则，序号为3，这个规则允许对端口&lt;code&gt;8080&lt;/code&gt;的连接，你可以使用下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw delete 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;删除规则的第二种方法就是指定实际的规则。例如，如果你添加过一个打开端口&lt;code&gt;8069&lt;/code&gt;的规则，你可以通过下面的命令删除它：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw delete allow 8069
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;禁用ufw&#34;&gt;禁用UFW&lt;/h2&gt;
&lt;p&gt;如果因为任何原因，你需要停止 UFW，并且使得所有规则失效，你可以运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw disable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;稍后，如果你想重新启用 UFW，并且激活所有规则，输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw enable
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;重置ufw&#34;&gt;重置UFW&lt;/h2&gt;
&lt;p&gt;重置 UFW 将会禁用 UFW，删除所有激活的规则。如果你想撤销所有的应用规则，并且重新开始时，这个很有用。&lt;/p&gt;
&lt;p&gt;想要重置 UFW，简单输入下面的命令:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw reset
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;ip伪装&#34;&gt;IP伪装&lt;/h2&gt;
&lt;p&gt;IP 伪装是一种在 Linux内核中的 NAT（网络地址转换），它通过重写源 IP 和目标 IP 的地址和端口，来转换网络流量。使用 IP 伪装技术，你可以允许局域网中的一台或者多台机器，和互联网进行交互，其中的一台 Linux 机器扮演网关。&lt;/p&gt;
&lt;p&gt;使用 UFW 配置 IP 伪装需要几个步骤：&lt;/p&gt;
&lt;p&gt;首先，你需要启用 IP 转发。想要这么做，打开&lt;code&gt;/etc/ufw/sysctl.conf&lt;/code&gt;文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nano /etc/ufw/sysctl.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查找并且取消这一行的注释&lt;code&gt;net.ipv4.ip_forward = 0&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net/ipv4/ip_forward=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下一步，你需要配置 UFW 来允许转发包。打开 UFW 配置文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nano /etc/default/ufw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定位到&lt;code&gt;DEFAULT_FORWARD_POLICY&lt;/code&gt;处，修改值从&lt;code&gt;DROP&lt;/code&gt;到&lt;code&gt;ACCEPT&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DEFAULT_FORWARD_POLICY=&amp;quot;ACCEPT&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在你需要设置在&lt;code&gt;nat&lt;/code&gt;表和伪装规则中默认的&lt;code&gt;POSTROUTING&lt;/code&gt;策略。想要这么做，打开&lt;code&gt;/etc/ufw/before.rules&lt;/code&gt;文件，并且附加下面的行进去，像下面这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nano /etc/ufw/before.rules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;附加下面的行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic through eth0 - Change to public network interface
-A POSTROUTING -s 10.8.0.0/16 -o eth0 -j MASQUERADE

# don&#39;t delete the &#39;COMMIT&#39; line or these rules won&#39;t be processed
COMMIT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不要忘记将&lt;code&gt;-A POSTROUTING&lt;/code&gt;一行中的&lt;code&gt;eth0&lt;/code&gt;替换成你的公开网络接口的名字：&lt;/p&gt;
&lt;p&gt;当你做完这些，保存，并且关闭文件。&lt;/p&gt;
&lt;p&gt;最后，通过禁用，重新启用 UFW，重载 UFW 规则。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw disable
sudo ufw enable
&lt;/code&gt;&lt;/pre&gt;
">linux的ufw防火墙使用</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/picgo-minio/"" data-c="
          &lt;h3 id=&#34;minio配置&#34;&gt;minio配置&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://www.minio.org.cn/docs/minio/linux/index.html&#34;&gt;minio安装参考官方文档，写的很好，点击此处&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;minio创建用户&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304270014249.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;minio创建存储桶&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304270009846.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304270013663.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;picgo下载安装及配置&#34;&gt;picGo下载安装及配置&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://picgo.github.io/PicGo-Doc/zh/&#34;&gt;picGo下载源链接&lt;/a&gt;&lt;br&gt;
点击Assets展开下载链接&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304270018286.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进入链接后，点击【快速开始】&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304262338351.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;打开picGo软件下载minio插件&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304262343385.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;点击picGo的【图床设置】&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304262346832.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;开始在picGo中配置minio对象存储桶的信息&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304262354518.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;strong&gt;排错小技巧&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;如果配置过程中有出现其他错误，可以通过查看日志方式进行排查（日志级别设置为【最高】或【全部】）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304262356850.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
">picgo+minio制作图床</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/github-cloudflare-CDN/"" data-c="
          &lt;h2 id=&#34;域名解析github的ip地址获取&#34;&gt;域名解析github的IP地址获取&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;github的IPv4地址获取&lt;a href=&#34;https://help.github.com/en/github/working-with-github-pages/managing-a-domain-for-your-github-pages-sit&#34;&gt;点击这里&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;地址栏输入github.com，然后按F12, 进入开发者模式，点击【网络】，若没有出现资源，则刷新页面即可。点击每个资源项，会出现对应ip地址，配置到自己域名A记录中即可。&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304262110988.png&#34; alt=&#34;查找github的IPv4地址&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;域名记录的相关说明&#34;&gt;域名记录的相关说明&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;记录名&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;示例：sub.example.com. IN CNAME hoge.example.com.&lt;br&gt;解释：【别名】 IN CNAME 【原名】&lt;/td&gt;
&lt;td&gt;给某一个domain起多个名字，或者，将当前的域名解析到另外一个域名中，A记录则是将域名解析为另一个IPv4地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;示例：sub.example.com. IN A 192.168.0.5&lt;br&gt;解释：将一个域名映射到一个对应公网IPv4地址&lt;/td&gt;
&lt;td&gt;添加 A 记录可将域名指向一个 IP 地址（外网地址）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MX&lt;/td&gt;
&lt;td&gt;MX记录（Mail Exchange）：邮件路由记录&lt;br&gt;在DNS上设定，用于将邮箱地址@符号后的域名指向邮件服务器&lt;br&gt;示例：example.com. IN MX 10 mail.example.com.&lt;br&gt;解释：【domain】 IN MX 【优先度】 【邮件服务器】&lt;/td&gt;
&lt;td&gt;当发信侧服务器给受信侧发邮件时，首先会要求DNS服务器解析受信侧邮箱地址中@后面部分的域名对应的MX记录（DNS的写法可以理解成example.com 的A记录下面，有一行上面示例的MX记录，当然邮箱服务器也有对应的A记录）。这样，邮件就直接发到对应的MX记录的A记录里的IP了。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NS&lt;/td&gt;
&lt;td&gt;指定域名解析服务器。&lt;br&gt;示例：example.com. IN NS ns1.example.com.&lt;br&gt;解释：【domain】 IN NS 【DNS服务器】&lt;/td&gt;
&lt;td&gt;指定该域名由哪个DNS服务器来进行解析。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;示例：ns1.exmaple.com. IN TXT &amp;quot;联系电话：XXXX&amp;quot;&lt;br&gt;解释：【domain】 IN TXT 【任意字符串】&lt;/td&gt;
&lt;td&gt;一般指某个主机名或域名的说明，或者联系方式，或者标注提醒等等。(阿里云中，可以在一级域名中通过该字段添加对应二级域名)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;重定向，有&lt;a href=&#34;https://cloud.tencent.com/document/product/302/12649&#34;&gt;隐性重定向和显性重定向&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;将一个域名指向另外一个已经存在的站点时，需要添加 URL 记录。（与CNAME类似）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SPF&lt;/td&gt;
&lt;td&gt;参考https://www.renfei.org/blog/introduction-to-spf.html&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;检查自己有没配置成功cloudflare的cdn加速&#34;&gt;检查自己有没配置成功cloudflare的CDN加速&lt;/h3&gt;
&lt;p&gt;我们可以自己查询下域名服务器，以确定是否设置生效。CMD 运行 nslookup -qt=ns 域名 -&amp;gt; 回车，nameserver 后面的就是域名服务器地址。&lt;br&gt;
&lt;a href=&#34;https://zhuanlan.zhihu.com/p/97389072&#34;&gt;点击此处查看详细信息&lt;/a&gt;&lt;/p&gt;
">github+阿里域名+cloudflare</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/linux_kernel_compiler/"" data-c="
          &lt;h2 id=&#34;内核编译准备&#34;&gt;内核编译准备&lt;/h2&gt;
&lt;hr&gt;
&lt;h4 id=&#34;imx6ull内核资料下载参考博客&#34;&gt;imx6ull内核资料下载参考博客&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.csdn.net/ZHONGCAI0901/article/details/118705741&#34;&gt;内核源码和资料下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;linux-内核下载地址&#34;&gt;Linux 内核下载地址&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;官方：&lt;a href=&#34;https://mirrors.edge.kernel.org/pub/linux/kernel/&#34;&gt;点击下拉最下面为内核源码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;国内：&lt;a href=&#34;https://mirror.bjtu.edu.cn/kernel/linux/kernel/&#34;&gt;点击下拉最下面为内核源码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;编译好的镜像文件：&lt;a href=&#34;https://kernel.ubuntu.com/~kernel-ppa/mainline/&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;交叉编译工具&#34;&gt;交叉编译工具&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;下载：&lt;a href=&#34;https://developer.arm.com/downloads/-/gnu-a&#34;&gt;交叉编译工具&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;下载：&lt;a href=&#34;https://launchpad.net/gcc-arm-embedded/+download&#34;&gt;更老的交叉编译工具链&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;如果64位机器运行老的交叉编译工具，可能会出现&lt;code&gt;bash: /home/learn/software/tools/linux-embedded/bin/arm-none-eabi-gcc: No such file or directory&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;原因：老的交叉编译工具为32位，需要下载下面安装包，使64位系统支持运行32位交叉工具&lt;br&gt;
&lt;code&gt;sudo apttitude update&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sudo apttitude install lib32z1&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sudo apttitude install lib32ncurses5-dev&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sudo apttitude install libc6-dev-i386&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;交叉编译工具概述&#34;&gt;交叉编译工具概述：&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;gnueabi和gnueabihf的区别(硬浮点、软浮点)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;gcc-arm-linux-gnueabi – The GNU C compiler for armel architecture&lt;br&gt;
gcc-arm-linux-gnueabihf – The GNU C compiler for armhf architecture&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可见这两个交叉编译器&lt;strong&gt;适用于arm-el和arm-hf两个不同的架构, 在对待浮点运算采取了不同的策略&lt;/strong&gt;，其实这两个交叉编译器只不过是gcc的选项-mfloat-abi的默认值不同. gcc的选项-mfloat-abi有三种值soft,softfp,hard(其中后两者都要求arm里有fpu浮点运算单元,soft与后两者是兼容的，但softfp和hard两种模式互不兼容)：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;soft&lt;/strong&gt; :不用fpu进行浮点计算，即使有fpu浮点运算单元也不用,而是使用软件模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;softfp&lt;/strong&gt; :armel架构(对应的编译器为gcc-arm-linux-gnueabi)采用的默认值，用fpu计算，但是传参数用普通寄存器传，这样中断的时候，只需要保存普通寄存器，中断负荷小，但是参数需要转换成浮点的再计算。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hard&lt;/strong&gt; :armhf架构(对应的编译器为gcc-arm-linux-gnueabihf)采用的默认值，用fpu计算，传参数也用fpu中的浮点寄存器传，省去了转换, 性能最好，但是中断负荷高。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;编译操作步骤&#34;&gt;编译操作步骤&lt;/h3&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;复制源码到linux系统中的自定义文件夹下，并解压&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scp -r ip地址:内核源码路径  本地存放的路径 
tar -zxvf linux-5.4.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;tar.gz&lt;/code&gt; 是 &lt;code&gt;gzip&lt;/code&gt; 格式的压缩包&lt;br&gt;
&lt;code&gt;# 解压命令&lt;/code&gt;&lt;br&gt;
&lt;code&gt;tar -zxvf 压缩包名.tar.gz -C 目标目录/&lt;/code&gt;&lt;br&gt;
&lt;code&gt;tar.xz&lt;/code&gt; 是 &lt;code&gt;xz&lt;/code&gt; 格式的压缩包&lt;br&gt;
&lt;code&gt; # 解压命令&lt;/code&gt;&lt;br&gt;
&lt;code&gt;tar -xvf 压缩包名.tar.xz -C 目标目录/&lt;/code&gt;&lt;br&gt;
&lt;code&gt;-z&lt;/code&gt; 表示：通过 gzip 过滤归档（用 gzip 解压）&lt;br&gt;
&lt;code&gt;-x&lt;/code&gt; 表示：从归档中解出文件（提取压缩包）&lt;br&gt;
&lt;code&gt;-v&lt;/code&gt; 表示：详细地列出处理的文件（打印一大堆信息）&lt;br&gt;
&lt;code&gt;-f&lt;/code&gt; 表示：使用归档文件或 ARCHIVE 设备（就是压缩文件）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内核源码文件夹说明&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;arch/&lt;/code&gt;：是linux内核支持的所有cpu体系架构（如：arm64，x86）---内核移植常用做修改代码&lt;br&gt;
&lt;code&gt;Documentation/&lt;/code&gt;：内核的参考文档&lt;br&gt;
&lt;code&gt;init/&lt;/code&gt;：内核启动的相关代码，main.c中start_kernel(void) 为内核启动c语言入口函数&lt;br&gt;
&lt;code&gt;block/&lt;/code&gt;：块设备相关代码&lt;br&gt;
&lt;code&gt;drivers/&lt;/code&gt;：设备驱动代码，占据70%代码量左右，（如：i2c，mmc等）---驱动移植常用，可以修改其中与自己相似的代码作为自己设备的驱动程序&lt;br&gt;
&lt;code&gt;ipc/&lt;/code&gt;：进程通信相关代码&lt;br&gt;
&lt;code&gt;security/&lt;/code&gt;：系统安全相关代码&lt;br&gt;
&lt;code&gt;net/&lt;/code&gt;：网络协议栈相关代码&lt;br&gt;
&lt;code&gt;sound/&lt;/code&gt;：声音播放器相关代码&lt;br&gt;
&lt;code&gt;fs/&lt;/code&gt;：文件系统相关代码，（如：jffs2：nor flash用的；yaffs：nand flash用的；sysfs：虚拟文件系统；ntfs：window和linux可用的文件系统）&lt;br&gt;
&lt;code&gt;kernel/&lt;/code&gt;：内核核心相关代码，（如：进程管理，和进程调度）&lt;br&gt;
&lt;code&gt;include/&lt;/code&gt;：内核头文件&lt;br&gt;
&lt;code&gt;mm/&lt;/code&gt;：内核内存管理相关代码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置内核源码&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生成一个.config配置文件，方便编译过程中确定编译的方向为x86还是arm64&lt;/li&gt;
&lt;li&gt;环境变量.bashrc，.profile，.bash_profile区别：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/405174594&#34;&gt;参考知乎文章&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;//设置自己开发版对应的cpu架构，arm或其他（也可以直接配置到环境变量中）
export ARCH=x86
export CROSS_COMPILE=arm-none-linux-gnueabihf-    // (x86不用交叉编译工具链，此步骤可以忽略)
export PATH=$PATH:交叉编译工具的bin目录 （如：/home/software/tools/gcc-arm-10.2-2020.11-aarch64-arm-none-linux-gnueab/bin） 
# 制作开发板对应的配置文件
# defconfig：一般位于linux-4.15.1/arch/$ARCH/configs/这个目录下
make x86_64_defconfig
#配置内核，通过图形化界面，（`空格`：进行选中），可以配置内核的块设备大小等
make menuconfig
# 使用make menuconfig如果是ubuntu要先安装libncurses5-dev，因为ubuntu默认没有安装menuconfig
sudo apt-get install libncurses5-dev

# make menuconfig 的配置项（配置支持ramdisk启动）
General setup ----&amp;gt;
        ----&amp;gt;   [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
Device Drivers ----&amp;gt;
    [*] Block devices ----&amp;gt;     # 表示把这个驱动编译到内核里
        &amp;lt;*&amp;gt; RAM block device support
        (65535) Default RAM disk size (kbytes)

# 编译内核：编译完 Image的路径一般保存在 linux-4.15.1/arch/$ARCH/uboot/这个目录下
make
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;amd 32位X86&lt;br&gt;
amd64 64位X86&lt;br&gt;
arm 32位ARM&lt;br&gt;
arm64 64位ARM&lt;br&gt;
&lt;strong&gt;注意&lt;/strong&gt;：编译成静态链接库，可以不依赖任何动态链接库，可以方便移动到其他文件夹或平台&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;编译内核源码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安装内核&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重启机器，验证版本&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;编译问题解决&#34;&gt;编译问题解决&lt;/h3&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li&gt;&amp;lt;openssl/bio.h&amp;gt; 头文件找不到
&lt;ul&gt;
&lt;li&gt;问题描述&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202305152048899.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;解决方案&lt;br&gt;
&lt;code&gt;sudo apt-get install libssl-dev&lt;/code&gt;&lt;br&gt;
libssl-dev包含libraries, header files and manpages，他是openssl的一部分，而openssl对ssl进行了实现～&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;busybox工具编译制作文件系统&#34;&gt;busybox工具编译制作文件系统&lt;/h2&gt;
&lt;h3 id=&#34;编译准备&#34;&gt;编译准备&lt;/h3&gt;
&lt;h4 id=&#34;linux-中的-busybox-是什么如何使用它摘自知乎&#34;&gt;Linux 中的 BusyBox 是什么？如何使用它？(摘自知乎)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;BusyBox 是一个开源项目，它提供了大约 400 个常见 UNIX/Linux 命令的精简实现。

BusyBox 实现删除了不常见的、很少使用的命令选项，一切都小于 1 MB，这个最小的图像是它在嵌入式系统和物联网领域以及云计算世界中流行的原因。

不要看它的大小，BusyBox像经典编辑器一样具有 sed 和 awk 的范围（再次精简版），它也包含自己的 shell，它甚至包含一个可以作为 PID 1 启动的 init 命令，这意味着 BusyBox 可以配置为 Systemd、OpenRC 等的替代品。

BusyBox 是 GNU Coreutils 的绝佳替代品，特别是在操作系统的小尺寸很重要的情况下。

BusyBox 为您提供流行的 Linux 命令，如 mv、mkdir、ls 等，但它仅包含这些命令的常用选项。这种极简主义是 BusyBox 的 USP。

&amp;gt; 通俗来说：就是一种用来制作linux文件系统的工具
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;下载&#34;&gt;下载&lt;/h4&gt;
&lt;p&gt;busybox源码下载路径：&lt;a href=&#34;https://www.busybox.net/&#34;&gt;点击busybox源码下载&lt;/a&gt;&lt;br&gt;
busybox编译根文件系统参考博客：&lt;a href=&#34;https://blog.csdn.net/assiduous_me/article/details/120982546&#34;&gt;点击此处查看&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;编译安装&#34;&gt;编译安装&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;解压busybox源码&lt;pre&gt;&lt;code&gt; tar -xvf busybox-1.30.0.tar.bz2
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;配置busybox源码&lt;pre&gt;&lt;code&gt;make menuconfig
Settings
     build options
         [*] build busybox as a static binary (no shared libs)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;编译和安装&lt;pre&gt;&lt;code&gt;# 编译后的目录文件存放再 _install文件夹下面
make &amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;_install目录下的文件和命令通过软链接方式访问&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87%E7%BC%96%E8%BE%91_20230507231259.jpg&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;li&gt;编译后的文件系统目录详细说明&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;根文件系统配置制作&#34;&gt;根文件系统配置制作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; 切换目录到：./busybox1.32.1/_install/
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;文件系统目录创建（根据linux系统目录创建相应的文件夹）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建文需要的文件夹&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;     mkdir etc dev mnt
     mkdir  -p proc sys tmp
     mkdir etc/init.d或 mkdir -p etc/init.d
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;编辑系统必须的文件信息&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;vim etc/fstab：编辑文件系统挂载目录信息&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    proc /proc proc defaults 0 0
    tmpfs /tmp tmpfs defaults 0 0
    sysfs /sys sysfs defaults 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;vim etc/init.d/rcS：编辑 rcS文件&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;echo -e &amp;quot;Welcome to tinyLinux&amp;quot;          # 提示信息
/bin/mount
echo -e &amp;quot;Remounting the root filesystem&amp;quot;    # 提示信息
mount -o remount,rw /
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev &amp;gt; /proc/sys/kernel/hotplug
mdev -s
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;设置rcS文件设置可执行权限&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;chmod 755 etc/init.d/rcS
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;vim etc/inittab：编辑inittab文件&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    ::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::askfirst:-/bin/sh
    ::ctrlaltdel:/bin/umount -a -r
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;设置文件权限为可执行&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;chmod 755 etc/inittab
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;添加设备节点和文件&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    cd dev/
    # 创建控制台设备节点，c：字符设备；5：主设备号；1：次设备号
    mknod console c 5 1   
    # 创建空设备
    mknod null c 1 3
    # 创建tty终端设备；
    mknod tty1 c 4 1
    # 创建ttyS0终端设备
    mknod ttyS0 c 4 64
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;终端，控制台等区别：&lt;a href=&#34;https://www.zhihu.com/question/267699446&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;制作根文件系统镜像文件（内核可以识别的）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目录可以选择自定义目录，主要用于存放镜像文件路径&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;先制作一个空的镜像文件；&lt;/li&gt;
&lt;li&gt;然后把此镜像文件格式化为ext3格式；&lt;/li&gt;
&lt;li&gt;然后把此镜像文件挂载，并把根文件系统复制到挂载目录；&lt;/li&gt;
&lt;li&gt;卸载该镜像文件；&lt;/li&gt;
&lt;li&gt;打包成gzip；&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;#!/bin/bash
rm -rf rootfs.ext3
rm -rf fs
# 创建空的镜像文件
dd if=/dev/zero of=./rootfs.ext3 bs=1M count=32

# 格式化镜像文件为ext3
mkfs.ext3 rootfs.ext3
mkdir fs

# 此镜像文件挂载
mount -o loop rootfs.ext3 ./fs

# 并把根文件系统复制到挂载目录
cp -rf ./_install/* ./fs

# 卸载该镜像文件
umount ./fs

# 打包成gzip
gzip --best -c rootfs.ext3 &amp;gt; rootfs.img.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终生成的文件系统镜像名字为：rootfs.img.gz&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;安装编译问题解决&#34;&gt;安装编译问题解决&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;make过程中出现如下错误&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;LINK    busybox_unstripped
Static linking against glibc, can&#39;t use --gc-sections
Trying libraries: crypt m resolv
Failed: -Wl,--start-group  -lcrypt -lm -lresolv  -Wl,--end-group
Output of:
arm-none-linux-gnueabihf-gcc -Wall -Wshadow -Wwrite-strings -Wundef shell/built-in.o sysklogd/built-in.o util-linux/built-in.o util-linux/volume_id/built-in.o -Wl,--end-group -Wl,--start-group -lcrypt -lm -lresolv -Wl,--end-group
/home/learn/software/tools/linux-gnueabihf/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/../lib/gcc/
/home/learn/software/tools/linux-gnueabihf/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/../lib/gcc/arm-none-linux-gnueabihf/10.3.1/../../../../arm-none-linux-gnueabihf/bin/ld: util-linux/lib.a(rdate.o): in function `rdate_main&#39;:rdate.c:(.text.rdate_main+0x80): undefined reference to `stime&#39;
/home/learn/software/tools/linux-gnueabihf/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/../lib/gcc/arm-none-linux-gnueabihf/10.3.1/../../../../arm-none-linux-gnueabihf/bin/ld: coreutils/lib.a(date.o): in function `date_main&#39;:&amp;gt;date.c:(.text.date_main+0x152): undefined reference to `stime&#39;
collect2: error: ld returned 1 exit status
Note: if build needs additional libraries, put them in CONFIG_EXTRA_LDLIBS.
Example: CONFIG_EXTRA_LDLIBS=&amp;quot;pthread dl tirpc audit pam&amp;quot;
make: *** [Makefile:718: busybox_unstripped] Error 1
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;解决方法：
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;glibc&lt;/code&gt;在&lt;code&gt;2.31（ubuntu20.2）&lt;/code&gt;中已经过时被移除了，如果想继续Ubuntu20.2以上版本配合busybox使用，请使用&lt;code&gt;busybox1.32&lt;/code&gt;以上版本（针对此问题已经发布相应的补丁了）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;mount -o loop src dest 报如下错误&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;mount -o loop rootfs.ext3 ./fs 报错mount: ./fs: failed to setup loop device for /home/learn/software/busybox/busybox-1.32.1/rootfs.ext
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;问题原因&lt;br&gt;
&lt;code&gt;dd if=/dev/zero of=./rootfs.ext3 bs=1M count=32&lt;/code&gt; 此命令只是用0来填充初始化./rootfs.ext3文件，&lt;code&gt;./rootfs.ext3&lt;/code&gt;此时不是一个块设备，如果用mount -o loop命令，则会报错；因为mount -o loop 在内部已经默认的将文件和 /dev/loop0 挂载，而/dev/loop0可能已经被其他设备占用，空间不足&lt;/li&gt;
&lt;li&gt;解决办法&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;    # 将rootfs.ext3转为块设备
    losetup -f    # 列出第一个没有使用的设备块
    losetup /dev/loop5 ./rootfs.ext3   # 将./rootfs.ext3转为块文件

    # 卸载loop块的挂载
    losetup -d /dev/loop1

    # 查看设备块对应的设备块
    lsblk|grep loop5 
    或 losetup -a
&lt;/code&gt;&lt;/pre&gt;
">linux内核编译</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/linux-partition/"" data-c="
          &lt;h2 id=&#34;自带disks进行分区操作&#34;&gt;自带disks进行分区操作：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.csdn.net/SGuniver_22/article/details/122236807&#34;&gt;ubuntu自带Disks工具进行分区操作链接&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;gparted工具进行分区操作&#34;&gt;gparted工具进行分区操作：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.csdn.net/Chen_qi_hai/article/details/108814596&#34;&gt;gparted进行ubuntu分区操作链接&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;采用终端进行分区操作&#34;&gt;采用终端进行分区操作：&lt;/h2&gt;
&lt;p&gt;分区操作，以东芝移动硬盘+树莓派为例&lt;/p&gt;
&lt;h3 id=&#34;东芝移动硬盘&#34;&gt;东芝移动硬盘&lt;/h3&gt;
&lt;p&gt;相关软件下载：&lt;a href=&#34;https://toshiba-semicon-storage.com/cn/canvio/support/download.html&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;h5 id=&#34;附树莓派4的usb接口说明树莓派等开发板连接移动硬盘&#34;&gt;附树莓派4的USB接口说明（树莓派等开发板连接移动硬盘）&lt;/h5&gt;
&lt;p&gt;黑色或白色为usb2.0接口，蓝色为usb3.0&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304291657069.png&#34; alt=&#34;树莓派接口图&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;准备&#34;&gt;准备&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;查看当前linux是否支持移动硬盘的文件系统类型
&lt;ul&gt;
&lt;li&gt;移动硬盘的文件类型一般为NTFS，一般linux不支持NTFS文件系统的。（我的树莓派连接&lt;code&gt;ntfs&lt;/code&gt;文件系统的移动硬盘后，可以进行读写操作，但是使用chmod，chown，chgrp等命令不能修改文件或文件夹权限，需要配置&lt;code&gt;/etc/fstab&lt;/code&gt;文件统一配置权限，如何配置权限，请在本节末尾查看）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;findmnt：查看文件系统的信息，使用方式参考findmnt --help

blkid：来查看有哪些文件系统，使用方法参考 blkid --help

lsblk：查看当前可用或挂载的硬盘的文件系统（lsblk -f）

cat /proc/filesystems：查看内核支持的文件系统 
&lt;/code&gt;&lt;/pre&gt;
执行完上述任一条命令，若发现自己的linux系统支持ntfs的，则跳过第2步&lt;br&gt;
**注意：**想要在linux和windows都可以使用，推荐使用ntfs（大磁盘容量）或FAT32（小磁盘容量）的文件系统的硬盘（可以自己格式化磁盘的文件系统类型）&lt;/li&gt;
&lt;li&gt;如何让Linux系统挂载NTFS文件系统类型的硬盘或移动硬盘。
&lt;ul&gt;
&lt;li&gt;方法一：在linux系统中，下载NTFS文件系统的驱动，支持NTFS文件系统&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;先安装GCC&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install gcc
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;下载NTFS-3G插件：&lt;a href=&#34;http://www.tuxera.com/community/ntfs-3g-download&#34;&gt;点击下载&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;NTFS-3G插件安装操作步骤&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;cd /usr/local/src
进入下载的压缩包的所在目录

tar -zxvf ntfs-3g_ntfsprogs-2016.2.22.tgz 
解压

cd ntfs-3g_ntfsprogs-2016.2.22
进入解压后的目录

./configure 
编译前准备

make
编译

make install
安装
&lt;/code&gt;&lt;/pre&gt;
NTFS-3G插件安装完成之后，Linux系统就可以支持NTFS文件系统了。(Linux系统将NTFS文件系统类型识别为ntfs-3g)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;方法二：格式化移动硬盘格式
&lt;ol&gt;
&lt;li&gt;将磁盘格式化为linux系统支持的文件类型，如：xfs，ext系列等&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304291850159.png&#34; alt=&#34;格式化移动硬盘&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;
&lt;p&gt;linux挂载和卸载移动硬盘的命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;挂载移动硬盘或其他设备&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    # mount -t [文件类型] [设备节点] [挂载目录]
    mount -t ntfs-3g /dev/sda1 /var/data
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;卸载外设备或U盘&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    # umount -v /dev/sda1          通过设备名卸载  
    # umount -v /mnt/mymount/      通过挂载点卸载          
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;补充，卸载硬盘和U盘的命令为啥不一样的，推荐这个博客：&lt;a href=&#34;https://bulletforkiller.github.io/post/2020-08-16-linux-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%BC%B9%E5%87%BA%E5%8F%AF%E7%A7%BB%E5%8A%A8%E7%A3%81%E7%9B%98/&#34;&gt;Josephine&#39;s Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;问题排查&#34;&gt;问题排查&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;若使用fdisk -l命令找不到对应磁盘信息。可能存在的问题有两个。（其他的情况需要自己再慢慢踩坑补充）
&lt;ul&gt;
&lt;li&gt;插错USB口，如果是虚拟机更改设备USB接口为3.0及以上，如果是树莓派等，则改换usb3.0插口&lt;/li&gt;
&lt;li&gt;硬盘文件系统格式和linux不兼容，格式化硬盘&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;移动硬盘分区挂载&#34;&gt;移动硬盘分区挂载&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;磁盘逻辑卷分区推荐视频：&lt;a href=&#34;https://www.bilibili.com/video/BV1eM4y1N7MQ/?spm_id_from=333.999.0.0&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;br&gt;
linux的硬盘分区推荐视频：&lt;a href=&#34;https://www.bilibili.com/video/BV1kv411V78Y/?spm_id_from=333.999.0.0&amp;amp;vd_source=9752c2c90782ff8bd76174dfbc76c933&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;磁盘分区步骤&#34;&gt;磁盘分区步骤&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;查看本地已经存在的硬盘信息&lt;pre&gt;&lt;code&gt;sudo fdisk -l
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;为磁盘创建分区&lt;pre&gt;&lt;code&gt;sudo fdisk /dev/sdb    #/dev/sdb:是你移动硬盘的设备名称 --- 此命令进入磁盘分区
# 操作命令
 p：p命令查看打印当前磁盘的分区详细情况
 m：打印出help帮助信息
 n：创建分区（最多只能创建4个分区）
 # 根据提示信息进行分区操作
 d：删除一个分区
 w：保存内存中对磁盘分区的操作

 partprobe /dev/sda1：将分区信息写入内核中（忘记命令，直接输入part，会有提示）
 mkfs：命令格式化分区 （mkfs.xfs /dev/sdc1)---mkfs后面的.xfs为格式化的类型
 apt-get install xfsprogs：用于安装mkfs
&lt;/code&gt;&lt;/pre&gt;
创建分区操作（图示）&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304292235553.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;br&gt;
&lt;img src=&#34;https://33373sl116.zicp.fun:443/image-blog/202304292242632.png&#34; alt=&#34;&#34; loading=&#34;lazy&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;开机自动挂载命令的配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; # 永久自动挂载方式
 vim /etc/fstab
 # 首先查看UUID
 blkid
 # 将 /dev/vdb1 的 UUID 复制出来，然后写入到/etc/fstab中去
 echo &amp;quot;PARTUUID=88511c72-01 /home/pi/data ntfs defaults 0 0&amp;quot; &amp;gt;&amp;gt; /etc/fstab
 # 或者直接编辑 /etc/fstab
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PARTUUID=88511c72-01&lt;/th&gt;
&lt;th&gt;/home/pi/data&lt;/th&gt;
&lt;th&gt;ntfs&lt;/th&gt;
&lt;th&gt;defaults&lt;/th&gt;
&lt;th&gt;0&lt;/th&gt;
&lt;th&gt;0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;要挂载的设备分区设备号&lt;/td&gt;
&lt;td&gt;挂载的目录&lt;/td&gt;
&lt;td&gt;文件系统类型&lt;/td&gt;
&lt;td&gt;挂载选项&lt;/td&gt;
&lt;td&gt;是否备份&lt;/td&gt;
&lt;td&gt;是否检测&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;注意1：文件挂载后，普通用户可能存在没有权限，普通的chrgp,chmod,chown等命令修改权限无效，则可以直接修改 &amp;lt; /etc/fstab &amp;gt; 参考链接 : &lt;a href=&#34;https://www.cnblogs.com/iceman-/p/7835121.html&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;注意2：linux挂载ntfs文件系统，不能修改文件或文件夹权限，root用户也不行，原因和问题推荐jack_孟博主的文章：&lt;a href=&#34;https://www.cnblogs.com/mq0036/p/9343862.html&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;提示：查看用户（uid）和用户（GID），可以使用命令&lt;code&gt;cat /etc/passwd &lt;/code&gt; 或&lt;code&gt;id&lt;/code&gt;(id命令展示可能不全)&lt;/li&gt;
&lt;/ul&gt;
">linux磁盘分区</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/qemu/"" data-c="
          &lt;h2 id=&#34;安装编译&#34;&gt;安装编译&lt;/h2&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;官网下载链接：&lt;a href=&#34;https://www.qemu.org/download/&#34;&gt;点击此处&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;linux安装步骤&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;wget https://download.qemu.org/qemu-8.0.0.tar.xz
tar xvJf qemu-8.0.0.tar.xz
cd qemu-8.0.0
./configure
make
make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;错误解决&#34;&gt;错误解决&lt;/h2&gt;
&lt;hr&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;错误&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;解决方法&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Using &#39;./build&#39; as the directory for build output&lt;br&gt;WARNING: unrecognized host CPU, proceeding with &#39;uname -m&#39; output &#39;x86_64&#39;&lt;br&gt;ERROR: GNU make (make) not found&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sudo apt-get install make&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using &#39;./build&#39; as the directory for build output&lt;br&gt;WARNING: unrecognized host CPU, proceeding with &#39;uname -m&#39; output &#39;x86_64&#39;&lt;br&gt;ERROR: Cannot find Ninja&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sudo apt-get install Ninja&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using &#39;./build&#39; as the directory for build output&lt;br&gt;WARNING: unrecognized host CPU, proceeding with &#39;uname -m&#39; output &#39;x86_64&#39;&lt;br&gt;ERROR: &amp;quot;cc&amp;quot; either does not exist or does not work&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sudo apt-get install gcc&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using &#39;./build&#39; as the directory for build output&lt;br&gt;ERROR: pkg-config binary &#39;pkg-config&#39; not found&lt;/td&gt;
&lt;td&gt;** sudo apt-get install pkg-config**&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using &#39;./build&#39; as the directory for build output&lt;br&gt;ERROR: glib-2.56 gthread-2.0 is required to compile QEMU&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;apt-cache search glib2 看看应该安装哪个库&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;sudo apt-get install libglib2.0-dev&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Program iasl found：No&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sudo apt-get install iasl&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Program cgcc found: No&lt;/td&gt;
&lt;td&gt;直接输入cgcc会提示解决方案&lt;br&gt;&lt;strong&gt;sudo apt-get install sparse&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ibrary util found: YES&lt;br&gt;Run-time dependency appleframeworks found: NO (tried framework)&lt;br&gt;Found pkg-config: /usr/bin/pkg-config (0.29.2)&lt;br&gt;Run-time dependency gio-2.0 found: YES 2.72.4&lt;br&gt;Program /usr/bin/gdbus-codegen found: YES (/usr/bin/gdbus-codegen)&lt;br&gt;Run-time dependency gio-unix-2.0 found: YES 2.72.4&lt;br&gt;Run-time dependency pixman-1 found: NO (tried pkgconfig)&lt;br&gt;&lt;em&gt;../meson.build:555:2: ERROR: Dependency &amp;quot;pixman-1&amp;quot; not found, tried pkgconfig&lt;br&gt;A full log can be found at /home/learn/software/qemu/qemu-8.0.0/build/meson-logs/meson-log.txt&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;可以直接看最后的结果，会有错误提示百度搜索即可&lt;strong&gt;sudo apt-get install pixman&lt;/strong&gt;&lt;br&gt;如果失败了则采用新的install工具aptitude进行安装，这个工具如果安装失败了，会给出具体的解释&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Program scripts/decodetree.py found: YES (/usr/bin/python3 /home/learn/software/qemu/qemu-8.0.0/scripts/decodetree.py)&lt;br&gt;Program flex found: NO&lt;br&gt;../target/hexagon/meson.build:179:4: ERROR: Program &#39;flex&#39; not found or not executable&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sudo aptitude install flex&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Program bison found: NO&lt;br&gt;../target/hexagon/meson.build:185:4: ERROR: Program &#39;bison&#39; not found or not executable&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sudo aptitude install bison&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;常用排坑小技巧&#34;&gt;常用排坑小技巧&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;直接输入cgcc会提示解决方案&lt;/li&gt;
&lt;li&gt;可以直接看命令运行的最后的结果，会有错误提示百度搜索即可&lt;/li&gt;
&lt;li&gt;如果失败了则采用新的install工具aptitude进行安装，这个工具如果安装失败了，会给出具体的解&lt;br&gt;
释&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;安装系统要求建议&#34;&gt;安装系统要求（建议）&lt;/h3&gt;
&lt;p&gt;硬盘存储40GB（尽量大了好，不然编译过程中会出现磁盘root空间不足）&lt;br&gt;
处理器：2个（建议大于等于）&lt;br&gt;
内存：2GB（建议大于等于）&lt;br&gt;
提示：如果存储硬盘不足，可以用参考这个博客 &lt;a href=&#34;https://blog.csdn.net/tx20011613/article/details/110217516&#34;&gt;ubuntu 自带Disks工具进行磁盘分区操作&lt;/a&gt;&lt;/p&gt;
">QEMU安装踩坑日记</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/about-starling/"" data-c="
          &lt;blockquote&gt;
&lt;p&gt;欢迎来到我的小站呀，很高兴遇见你！🤝&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;关于本站&#34;&gt;🏠 关于本站&lt;/h2&gt;
&lt;p&gt;本站主要是作为个人日常学习历程记录，和个人觉得比较好的博客摘录。&lt;/p&gt;
&lt;h2 id=&#34;博主是谁&#34;&gt;👨‍💻 博主是谁&lt;/h2&gt;
&lt;p&gt;林星&lt;/p&gt;
&lt;h2 id=&#34;兴趣爱好&#34;&gt;⛹ 兴趣爱好&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;篮球&lt;/code&gt;&lt;br&gt;
&lt;code&gt;乒乓球 &lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;联系我呀&#34;&gt;📬 联系我呀&lt;/h2&gt;
&lt;p&gt;请在我的&lt;a href=&#34;https://gitee.com/xstarling/xstarling&#34;&gt;gitee的fairyblog仓库&lt;/a&gt;下提交&lt;a href=&#34;https://gitee.com/xstarling/xstarling&#34;&gt;Issues&lt;/a&gt;留言：&lt;a href=&#34;https://gitee.com/xstarling/xstarling&#34;&gt;点击此处到fairyblog仓库&lt;/a&gt;&lt;/p&gt;
&lt;figure data-type=&#34;image&#34; tabindex=&#34;1&#34;&gt;&lt;img src=&#34;https://gitee.com/xstarling/images/raw/master/blog/202403131032586.png&#34; alt=&#34;操作步骤&#34; loading=&#34;lazy&#34;&gt;&lt;/figure&gt;
">关于</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/hello-gridea/"" data-c="
          &lt;p&gt;👏  欢迎使用 &lt;strong&gt;Gridea&lt;/strong&gt; ！&lt;br&gt;
✍️  &lt;strong&gt;Gridea&lt;/strong&gt; 一个静态博客写作客户端。你可以用它来记录你的生活、心情、知识、笔记、创意... ...&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/getgridea/gridea&#34;&gt;Github&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://gridea.dev/&#34;&gt;Gridea 主页&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;http://fehey.com/&#34;&gt;示例网站&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;特性&#34;&gt;特性👇&lt;/h2&gt;
&lt;p&gt;📝  你可以使用最酷的 &lt;strong&gt;Markdown&lt;/strong&gt; 语法，进行快速创作&lt;/p&gt;
&lt;p&gt;🌉  你可以给文章配上精美的封面图和在文章任意位置插入图片&lt;/p&gt;
&lt;p&gt;🏷️  你可以对文章进行标签分组&lt;/p&gt;
&lt;p&gt;📋  你可以自定义菜单，甚至可以创建外部链接菜单&lt;/p&gt;
&lt;p&gt;💻  你可以在 &lt;strong&gt;Windows&lt;/strong&gt;，&lt;strong&gt;MacOS&lt;/strong&gt; 或 &lt;strong&gt;Linux&lt;/strong&gt; 设备上使用此客户端&lt;/p&gt;
&lt;p&gt;🌎  你可以使用 &lt;strong&gt;𝖦𝗂𝗍𝗁𝗎𝖻 𝖯𝖺𝗀𝖾𝗌&lt;/strong&gt; 或 &lt;strong&gt;Coding Pages&lt;/strong&gt; 向世界展示，未来将支持更多平台&lt;/p&gt;
&lt;p&gt;💬  你可以进行简单的配置，接入 &lt;a href=&#34;https://github.com/gitalk/gitalk&#34;&gt;Gitalk&lt;/a&gt; 或 &lt;a href=&#34;https://github.com/SukkaW/DisqusJS&#34;&gt;DisqusJS&lt;/a&gt; 评论系统&lt;/p&gt;
&lt;p&gt;🇬🇧  你可以使用&lt;strong&gt;中文简体&lt;/strong&gt;或&lt;strong&gt;英语&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;🌁  你可以任意使用应用内默认主题或任意第三方主题，强大的主题自定义能力&lt;/p&gt;
&lt;p&gt;🖥  你可以自定义源文件夹，利用 OneDrive、百度网盘、iCloud、Dropbox 等进行多设备同步&lt;/p&gt;
&lt;p&gt;🌱 当然 &lt;strong&gt;Gridea&lt;/strong&gt; 还很年轻，有很多不足，但请相信，它会不停向前 🏃&lt;/p&gt;
&lt;p&gt;未来，它一定会成为你离不开的伙伴&lt;/p&gt;
&lt;p&gt;尽情发挥你的才华吧！&lt;/p&gt;
&lt;p&gt;😘 Enjoy~&lt;/p&gt;
">Gridea使用说明</a>
      </div>
      
      <div class="item">
        <a class="result-title" style="opacity: 0;" href="https://xstarling.gitee.io/website-fairy/"" data-c="
          &lt;blockquote&gt;
&lt;p&gt;欢迎来到我的程序员常用网站推荐小站点，很高兴遇见你！🤝&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;️-程序员导师&#34;&gt;✍️ 程序员导师&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.itcoder.tech/&#34;&gt;linux常见问题解决&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.blog/&#34;&gt;国外答疑网站(stackoverflow)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://unix.stackexchange.com/&#34;&gt;linux OS 答疑&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.jianshu.com/&#34;&gt;简书博客网站&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://juejin.cn/&#34;&gt;掘金博客网站&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.51cto.com/&#34;&gt;51CTO博客网站&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/&#34;&gt;博客园网站&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;博主代码小仓库&#34;&gt;👨‍💻 博主代码小仓库&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gitee.com/xstarling&#34;&gt;gitee代码托管平台&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/xstarling&#34;&gt;github代码托管平台&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;嵌入式开发工具&#34;&gt;👨‍💻 嵌入式开发工具&lt;/h3&gt;
&lt;h4 id=&#34;linux内核源码-镜像&#34;&gt;Linux内核源码、镜像&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;国外：&lt;a href=&#34;https://mirrors.edge.kernel.org/pub/linux/kernel/&#34;&gt;点击下拉最下面为内核源码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;国内：&lt;a href=&#34;https://mirror.bjtu.edu.cn/kernel/linux/kernel/&#34;&gt;点击下拉最下面为内核源码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;编译好的镜像文件：&lt;a href=&#34;https://kernel.ubuntu.com/~kernel-ppa/mainline/&#34;&gt;点击此处&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;交叉编译工具&#34;&gt;交叉编译工具&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.arm.com/downloads/-/gnu-a&#34;&gt;交叉编译工具&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://launchpad.net/gcc-arm-embedded/+download&#34;&gt;老版本的交叉编译工具链&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;辅助小工具&#34;&gt;💼 辅助小工具&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.draw.io/index.html&#34;&gt;draw.io绘图工具&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cron.qqe2.com/&#34;&gt;在线cron表达式生成&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.jyshare.com/front-end/854/&#34;&gt;在线正则表达测试工具&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
">网站推荐小站点</a>
      </div>
      
    </div>
    <div class="page">
      <div id="page_ul"></div>
    </div>
  </div>
</div>
<script>
  !function () {
    let searchMask = document.querySelector('#search_mask');
    let result = document.querySelector('#result');
    let items = document.querySelectorAll('.item');
    let searchBox = document.querySelector('#search');
    let statCount = document.querySelector('#stat_count');
    let statTimes = document.querySelector('#stat_times');
    let pageUl = document.querySelector('#page_ul');
    let close = document.querySelector('#close');
    
    close.addEventListener('click', function() {
      searchMask.style = 'display: none;'
    })

    let finds = [];
    let contents = [];
    let pageSize = 10;
    items.forEach(item => {
      let a = item.querySelector('a');
      contents.push({
        title: a.innerText,
        details: a.dataset.c,
        link: a.href
      })
      item.remove();
    })

    function insertStr(soure, start, count) {
      let newStr = soure.substr(start, count);
      return soure.slice(0, start) + '<em>' + newStr + '</em>' + soure.slice(start + count);
    }

    pageUl.addEventListener('click', function(event) {
      let target = event.target;
      if (target.__proto__ === HTMLSpanElement.prototype) {
        appendResults(parseInt(target.dataset.i));
      }
    })

    function appendResults(index) {
      let htmlResult = '';
      let start = index || 0;
      let end = Math.min(start + pageSize, finds.length);
      for (let i = start; i < end; i++) {
        const current = finds[i];
        let html = current.title;
        let sum = 0;
        let positions = current.positions;
        positions.forEach(position => {
          html = insertStr(html, position.start + sum, position.count);
          sum += 9;
        })
        htmlResult += `<div class="item"><a class="result-title" href="${current.link}">${html}</a></div>`;
      }
      result.innerHTML = htmlResult;
      pageUl.innerHTML = '';
      let count = finds.length / pageSize;
      let lis = '';
      if (start !== 0) {
        lis += `<span class="fa fa-angle-left" data-i='${start - 1}'></span>`;
      }
      for (let i = 0; i < count; i++) {
        lis += `<span class='${i === start?'current':''}' data-i='${i}'>${i+1}</span>`;     
      }
      if (start+1 < count) {
        lis += `<span class="fa fa-angle-right" data-i='${start+1}'></span>`;  
      }
      pageUl.innerHTML = lis;
    }

    function search(delay) {
      let timer = null
      return function () {
        clearTimeout(timer)
        timer = setTimeout(() => {
          let start = Date.now();
          let segments = searchBox.value.split(' ').filter(c => c != '');
          if (segments.length <= 0) {
            return;
          }
          finds = [];
          let htmlResult = '';
          contents.forEach(content => {
            let title = content.title;
            let positions = [];
            let find = false;
            segments.forEach((segment) => {
              if (content.title.includes(segment)) {
                find = true;
                positions.push({
                  start: content.title.indexOf(segment),
                  count: segment.length
                })
              } else if (content.details.includes(segment)) {
                find = true;
              }
            });
            if (find) {
              finds.push({
                title: content.title,
                link: content.link,
                positions
              });
            }
          })
          appendResults(0);
          statCount.textContent = finds.length;
          statTimes.textContent = Date.now() - start;
        }, delay)
      }
    }
    searchBox.addEventListener('input', search(200));
  }()
</script>

<input hidden id="copy" />
<script>
  !function () {
    let times = document.querySelectorAll('.publish-time');
    for (let i = 0; i < times.length; i++) {
      let date = times[i].dataset.t;
      let time = Math.floor((new Date().getTime() - new Date(date).getTime()) / 1000);
      if (time < 60) {
        str = time + '秒之前';
      } else if (time < 3600) {
        str = Math.floor(time / 60) + '分钟之前';
      } else if (time >= 3600 && time < 86400) {
        str = Math.floor(time / 3600) + '小时之前';
      } else if (time >= 86400 && time < 259200) {
        str = Math.floor(time / 86400) + '天之前';
      } else {
        str = times[i].textContent;
      }
      times[i].textContent = str;
    }
  }();
</script>

<script>
  let language = '';
  if (language !== '') {
    let map = new Map();
    if (language === 'en') {
      map.set('search', 'Search');
      map.set('category', 'Categories');
      map.set('article', 'Articles');
      map.set('tag', 'Tags');
      map.set('top', 'Top');
      map.set('publish', 'published');
      map.set('minute', ' minutes');
      map.set('read-more', 'Read More');
      map.set('view', 'View');
      map.set('words', ' words');
      map.set('category-in', 'category in');
      map.set('preview', 'Meta');
      map.set('index', 'Toc');
      map.set('no-archives', "You haven't created yet");
      map.set('archives', " articles in total");
      map.set('cloud-tags', " tags in total");
      map.set('copyright', "Copyright: ");
      map.set('author', "Author: ");
      map.set('link', "Link: ");
      map.set('leave-message', "Leave a message");
      map.set('format', "Links Format");
      map.set('site-name', "Name: ");
      map.set('site-link', "Link: ");
      map.set('site-desc', "Desc: ");
      map.set('stat', " related results, taking ");
      map.set('stat-time', " ms");
      map.set('site-img', "Image: ");
    }

    if (map.size > 0) {
      let lanElems = document.querySelectorAll('.language');
      lanElems.forEach(elem => {
        let lan = elem.dataset.lan, text = map.get(lan);
        if (elem.__proto__ === HTMLInputElement.prototype) {
          elem.placeholder = text
        } else {
          if (elem.dataset.count) {
            text = elem.dataset.count + text;
          }
          elem.textContent = text;
        }
      })
    }
  }

  window.Clipboard = (function (window, document, navigator) {
    var textArea,
      copy;

    // 判断是不是ios端
    function isOS() {
      return navigator.userAgent.match(/ipad|iphone/i);
    }
    //创建文本元素
    function createTextArea(text) {
      textArea = document.createElement('textArea');
      textArea.value = text;
      textArea.style.width = 0;
      textArea.style.height = 0;
      textArea.clientHeight = 0;
      textArea.clientWidth = 0;
      document.body.appendChild(textArea);
    }
    //选择内容
    function selectText() {
      var range,
        selection;

      if (isOS()) {
        range = document.createRange();
        range.selectNodeContents(textArea);
        selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
        textArea.setSelectionRange(0, 999999);
      } else {
        textArea.select();
      }
    }

    //复制到剪贴板
    function copyToClipboard() {
      try {
        document.execCommand("Copy")
      } catch (err) {
        alert("复制错误！请手动复制！")
      }
      document.body.removeChild(textArea);
    }

    copy = function (text) {
      createTextArea(text);
      selectText();
      copyToClipboard();
    };

    return {
      copy: copy
    };
  })(window, document, navigator);

  function copyCode(e) {
    if (e.srcElement.tagName === 'SPAN' && e.srcElement.classList.contains('copy-code')) {
      let code = e.currentTarget.querySelector('code');
      var text = code.innerText;
      if (e.srcElement.textContent === '复制成功') {
        return;
      }
      e.srcElement.textContent = '复制成功';
      (function (elem) {
        setTimeout(() => {
          if (elem.textContent === '复制成功') {
            elem.textContent = '复制代码'
          }
        }, 1000);
      })(e.srcElement)
      Clipboard.copy(text);
    }
  }

  let pres = document.querySelectorAll('pre');
  pres.forEach(pre => {
    let code = pre.querySelector('code');
    let copyElem = document.createElement('span');
    copyElem.classList.add('copy-code');
    copyElem.textContent = '复制代码';
    pre.appendChild(copyElem);
    pre.onclick = copyCode
  })

</script>
<script src="/media/js/motion.js"></script>


  <script
    src="https://cdn.jsdelivr.net/gh/cferdinandi/smooth-scroll/dist/smooth-scroll.polyfills.min.js"></script>
  <script>
    var scroll = new SmoothScroll('a[href*="#"]', {
      speed: 200
    });
  </script>



<script src="/media/js/mouse/peace.js"></script>


  <script src="/media/js/cool.js"></script>




</html>